brasero r649 - in trunk: po src



Author: philippr
Date: Mon Feb 25 20:02:02 2008
New Revision: 649
URL: http://svn.gnome.org/viewvc/brasero?rev=649&view=rev

Log:
Forgotten files

Added:
   trunk/src/brasero-io.c
   trunk/src/brasero-io.h
Removed:
   trunk/src/brasero-vfs.c
Modified:
   trunk/po/POTFILES.in

Modified: trunk/po/POTFILES.in
==============================================================================
--- trunk/po/POTFILES.in	(original)
+++ trunk/po/POTFILES.in	Mon Feb 25 20:02:02 2008
@@ -91,3 +91,7 @@
 src/brasero-eject-dialog.c
 src/brasero-file-filtered.c
 src/brasero-filter-option.c
+src/brasero-data-vfs.c
+src/brasero-io.c
+src/brasero-data-vfs.c
+src/brasero-io.c

Added: trunk/src/brasero-io.c
==============================================================================
--- (empty file)
+++ trunk/src/brasero-io.c	Mon Feb 25 20:02:02 2008
@@ -0,0 +1,2288 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * trunk
+ * Copyright (C) Philippe Rouquier 2008 <bonfire-app wanadoo fr>
+ * 
+ * trunk is free software.
+ * 
+ * You may redistribute it and/or modify it under the terms of the
+ * GNU General Public License, as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ * 
+ * trunk is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with trunk.  If not, write to:
+ * 	The Free Software Foundation, Inc.,
+ * 	51 Franklin Street, Fifth Floor
+ * 	Boston, MA  02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gi18n-lib.h>
+#include <glib/gstdio.h>
+
+#include <gio/gio.h>
+
+#ifdef BUILD_PLAYLIST
+#include <totem-pl-parser.h>
+#endif
+
+#include "burn-basics.h"
+#include "brasero-utils.h"
+
+#include "brasero-io.h"
+#include "brasero-metadata.h"
+#include "brasero-async-task-manager.h"
+
+typedef struct _BraseroIOPrivate BraseroIOPrivate;
+struct _BraseroIOPrivate
+{
+	GMutex *lock;
+
+	/* used for returning results */
+	GSList *results;
+	gint results_id;
+
+	/* used for metadata */
+	GSList *metadatas;
+
+	/* used to "buffer" some results returned by metadata.
+	 * It takes time to return metadata and it's not unusual
+	 * to fetch metadata three times in a row, once for size
+	 * preview, once for preview, once adding to selection */
+	GQueue *meta_buffer;
+
+	guint progress_id;
+	GSList *progress;
+};
+
+#define BRASERO_IO_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), BRASERO_TYPE_IO, BraseroIOPrivate))
+
+/* so far 2 metadata at a time has shown to be the best for performance */
+#define MAX_CONCURENT_META 	2
+#define MAX_BUFFERED_META	20
+
+struct _BraseroIOResultCallbackData {
+	gpointer callback_data;
+	guint ref;
+};
+typedef struct _BraseroIOResultCallbackData BraseroIOResultCallbackData;
+
+struct _BraseroIOJob {
+	gchar *uri;
+	BraseroIOFlags options;
+
+	const BraseroIOJobBase *base;
+	BraseroIOResultCallbackData *callback_data;
+};
+typedef struct _BraseroIOJob BraseroIOJob;
+
+#define BRASERO_IO_JOB(data)	((BraseroIOJob *) (data))
+
+struct _BraseroIOJobResult {
+	const BraseroIOJobBase *base;
+	BraseroIOResultCallbackData *callback_data;
+
+	GFileInfo *info;
+	GError *error;
+	gchar *uri;
+};
+typedef struct _BraseroIOJobResult BraseroIOJobResult;
+
+
+typedef void	(*BraseroIOJobProgressCallback)	(BraseroIOJob *job,
+						 BraseroIOJobProgress *progress);
+
+struct _BraseroIOJobProgress {
+	BraseroIOJob *job;
+	BraseroIOJobProgressCallback progress;
+
+	BraseroIOPhase phase;
+
+	guint files_num;
+	guint files_invalid;
+
+	guint64 read_b;
+	guint64 total_b;
+
+	guint64 current_read_b;
+	guint64 current_total_b;
+
+	gchar *current;
+};
+
+G_DEFINE_TYPE (BraseroIO, brasero_io, BRASERO_TYPE_ASYNC_TASK_MANAGER);
+
+/**
+ * That's the structure to pass the progress on
+ */
+
+static gboolean
+brasero_io_job_progress_report_cb (gpointer callback_data)
+{
+	BraseroIOPrivate *priv;
+	GSList *iter;
+
+	priv = BRASERO_IO_PRIVATE (callback_data);
+
+	g_mutex_lock (priv->lock);
+	for (iter = priv->progress; iter; iter = iter->next) {
+		BraseroIOJobProgress *progress;
+
+		progress = iter->data;
+
+		/* update our progress */
+		progress->progress (progress->job, progress);
+		progress->job->base->progress (progress->job->base->object,
+					       progress,
+					       progress->job->callback_data);
+	}
+	g_mutex_unlock (priv->lock);
+
+	return TRUE;
+}
+
+static void
+brasero_io_job_progress_report_start (BraseroIO *self,
+				      BraseroIOJob *job,
+				      BraseroIOJobProgressCallback callback)
+{
+	BraseroIOJobProgress *progress;
+	BraseroIOPrivate *priv;
+
+	priv = BRASERO_IO_PRIVATE (self);
+
+	if (!job->base->progress)
+		return;
+
+	progress = g_new0 (BraseroIOJobProgress, 1);
+	progress->job = job;
+	progress->progress = callback;
+
+	g_mutex_lock (priv->lock);
+	priv->progress = g_slist_prepend (priv->progress, progress);
+	if (!priv->progress_id)
+		priv->progress_id = g_timeout_add (500, brasero_io_job_progress_report_cb, self);
+	g_mutex_unlock (priv->lock);
+}
+
+static void
+brasero_io_job_progress_report_stop (BraseroIO *self,
+				     BraseroIOJob *job)
+{
+	BraseroIOPrivate *priv;
+	GSList *iter;
+
+	priv = BRASERO_IO_PRIVATE (self);
+	g_mutex_lock (priv->lock);
+	for (iter = priv->progress; iter; iter = iter->next) {
+		BraseroIOJobProgress *progress;
+
+		progress = iter->data;
+		if (progress->job == job) {
+			priv->progress = g_slist_remove (priv->progress, progress);
+			if (progress->current)
+				g_free (progress->current);
+
+			g_free (progress);
+			break;
+		}
+	}
+
+	if (!priv->progress) {
+		if (priv->progress_id) {
+			g_source_remove (priv->progress_id);
+			priv->progress_id = 0;
+		}
+	}
+
+	g_mutex_unlock (priv->lock);
+}
+
+const gchar *
+brasero_io_job_progress_get_current (BraseroIOJobProgress *progress)
+{
+	return g_strdup (progress->current);
+}
+
+guint
+brasero_io_job_progress_get_file_processed (BraseroIOJobProgress *progress)
+{
+	return progress->files_num;
+}
+
+guint64
+brasero_io_job_progress_get_read (BraseroIOJobProgress *progress)
+{
+	return progress->current_read_b + progress->read_b;
+}
+
+guint64
+brasero_io_job_progress_get_total (BraseroIOJobProgress *progress)
+{
+	return progress->total_b;
+}
+
+BraseroIOPhase
+brasero_io_job_progress_get_phase (BraseroIOJobProgress *progress)
+{
+	return progress->phase;
+}
+
+static void
+brasero_io_unref_result_callback_data (BraseroIOResultCallbackData *data,
+				       GObject *object,
+				       BraseroIODestroyCallback destroy,
+				       gboolean cancelled)
+{
+	if (!data)
+		return;
+
+	data->ref --;
+	if (data->ref > 0)
+		return;
+
+	if (destroy)
+		destroy (object,
+			 cancelled,
+			 data->callback_data);
+	g_free (data);
+}
+
+static void
+brasero_io_job_result_free (BraseroIOJobResult *result)
+{
+	if (result->info)
+		g_object_unref (result->info);
+
+	if (result->error)
+		g_error_free (result->error);
+
+	if (result->uri)
+		g_free (result->uri);
+
+	g_free (result);
+}
+
+static void
+brasero_io_job_free (BraseroIOJob *job)
+{
+	if (job->callback_data)
+		job->callback_data->ref --;
+
+	g_free (job->uri);
+	g_free (job);
+}
+
+static void
+brasero_io_job_destroy (BraseroAsyncTaskManager *self,
+			gpointer callback_data)
+{
+	BraseroIOJob *job = callback_data;
+
+	/* If a job is destroyed we don't call the destroy callback since it
+	 * otherwise it would be called in a different thread. All object that
+	 * cancel io ops are doing it either in destroy () and therefore handle
+	 * all destruction for callback_data or if they don't they usually don't
+	 * pass any callback data anyway. */
+	/* NOTE: usually threads are cancelled from the main thread/loop and
+	 * block until the active task is removed which means that if we called
+	 * the destroy () then the destruction would be done in the main loop */
+	brasero_io_job_free (job);
+}
+
+/**
+ * Used to return the results
+ */
+
+static gboolean
+brasero_io_return_result_idle (gpointer callback_data)
+{
+	BraseroIO *self = BRASERO_IO (callback_data);
+	BraseroIOResultCallbackData *data;
+	BraseroIOJobResult *result;
+	BraseroIOPrivate *priv;
+
+	priv = BRASERO_IO_PRIVATE (self);
+
+	g_mutex_lock (priv->lock);
+
+	if (!priv->results) {
+		priv->results_id = 0;
+		g_mutex_unlock (priv->lock);
+		return FALSE;
+	}
+
+	result = priv->results->data;
+	priv->results = g_slist_remove (priv->results, result);
+
+	g_mutex_unlock (priv->lock);
+
+	data = result->callback_data;
+	if (result->uri || result->info || result->error)
+		result->base->callback (result->base->object,
+					result->error,
+					result->uri,
+					result->info,
+					data? data->callback_data:NULL);
+
+	/* Else this is just to call destroy () for callback data */
+	brasero_io_unref_result_callback_data (data,
+					       result->base->object,
+					       result->base->destroy,
+					       FALSE);
+	brasero_io_job_result_free (result);
+	return TRUE;
+}
+
+static void
+brasero_io_queue_result (BraseroIO *self,
+			 BraseroIOJobResult *result)
+{
+	BraseroIOPrivate *priv;
+
+	priv = BRASERO_IO_PRIVATE (self);
+
+	/* insert the task in the results queue */
+	g_mutex_lock (priv->lock);
+	priv->results = g_slist_append (priv->results, result);
+	if (!priv->results_id)
+		priv->results_id = g_idle_add ((GSourceFunc) brasero_io_return_result_idle, self);
+	g_mutex_unlock (priv->lock);
+}
+
+static void
+brasero_io_return_result (BraseroIO *self,
+			  const BraseroIOJobBase *base,
+			  const gchar *uri,
+			  GFileInfo *info,
+			  GError *error,
+			  BraseroIOResultCallbackData *callback_data)
+{
+	BraseroIOJobResult *result;
+
+	/* even if it is cancelled we let the result go through to be able to 
+	 * call its destroy callback in the main thread. */
+
+	result = g_new0 (BraseroIOJobResult, 1);
+	result->base = base;
+	result->info = info;
+	result->error = error;
+	result->uri = g_strdup (uri);
+
+	if (callback_data) {
+		result->callback_data = callback_data;
+		callback_data->ref ++;
+	}
+
+	brasero_io_queue_result (self, result);
+}
+
+/**
+ * Used to push a job
+ */
+
+static void
+brasero_io_set_job (BraseroIOJob *job,
+		    const BraseroIOJobBase *base,
+		    const gchar *uri,
+		    BraseroIOFlags options,
+		    BraseroIOResultCallbackData *callback_data)
+{
+	job->base = base;
+	job->uri = g_strdup (uri);
+	job->options = options;
+
+	if (callback_data) {
+		job->callback_data = callback_data;
+		job->callback_data->ref ++;
+	}
+	else
+		job->callback_data = NULL;
+}
+
+static void
+brasero_io_push_job (BraseroIO *self,
+		     BraseroIOJob *job,
+		     const BraseroAsyncTaskType *type)
+{
+	if (job->options & BRASERO_IO_INFO_URGENT)
+		brasero_async_task_manager_queue (BRASERO_ASYNC_TASK_MANAGER (self),
+						  BRASERO_ASYNC_URGENT,
+						  type,
+						  job);
+	else if (job->options & BRASERO_IO_INFO_IDLE)
+		brasero_async_task_manager_queue (BRASERO_ASYNC_TASK_MANAGER (self),
+						  BRASERO_ASYNC_IDLE,
+						  type,
+						  job);
+	else
+		brasero_async_task_manager_queue (BRASERO_ASYNC_TASK_MANAGER (self),
+						  BRASERO_ASYNC_NORMAL,
+						  type,
+						  job);
+
+}
+
+/**
+ * This part deals with symlinks, that allows to get unique filenames by
+ * replacing any parent symlink by its target and check for recursive
+ * symlinks
+ */
+
+static gchar *
+brasero_io_check_for_parent_symlink (const gchar *escaped_uri,
+				     GCancellable *cancel)
+{
+	GFile *parent;
+    	gchar *uri;
+
+	parent = g_file_new_for_uri (escaped_uri);
+    	uri = g_file_get_uri (parent);
+
+	while (parent) {
+	    	GFile *tmp;
+		GFileInfo *info;
+
+		info = g_file_query_info (parent,
+					  G_FILE_ATTRIBUTE_STANDARD_TYPE ","
+					  G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
+					  G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
+					  G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,	/* don't follow symlinks */
+					  NULL,
+					  NULL);
+		if (!info)
+		    	break;
+
+		/* NOTE: no need to check for broken symlinks since
+		 * we wouldn't have reached this point otherwise */
+		if (g_file_info_get_is_symlink (info)) {
+			const gchar *target_path;
+		    	gchar *parent_uri;
+		    	gchar *new_root;
+			gchar *newuri;
+
+		    	parent_uri = g_file_get_uri (parent);
+			target_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
+			new_root = g_filename_to_uri (target_path, NULL, NULL);
+
+			newuri = g_strconcat (new_root,
+					      uri + strlen (parent_uri),
+					      NULL);
+
+		    	g_free (uri);
+		    	uri = newuri;	
+
+		    	g_object_unref (parent);
+		    	g_free (parent_uri);
+
+		    	parent = g_file_new_for_uri (new_root);
+			g_free (new_root);
+		}
+
+		tmp = parent;
+		parent = g_file_get_parent (parent);
+		g_object_unref (tmp);
+
+		g_object_unref (info);
+	}
+
+	if (parent)
+		g_object_unref (parent);
+
+	return uri;
+}
+
+static gchar *
+brasero_io_get_uri_from_path (GFile *file,
+			      const gchar *path)
+{
+	gchar *uri;
+
+	if (!g_path_is_absolute (path))
+		file = g_file_resolve_relative_path (file, path);
+	else
+		file = g_file_new_for_path (path);
+
+	if (!file)
+		return NULL;
+
+	uri = g_file_get_uri (file);
+	g_object_unref (file);
+	return uri;
+}
+
+static gboolean
+brasero_io_check_symlink_target (GFile *parent,
+				 GFileInfo *info,
+				 const gchar *escaped_uri)
+{
+	const gchar *target;
+	gchar *target_uri;
+	guint size;
+
+	target = g_file_info_get_symlink_target (info);
+    	if (!target)
+		return FALSE;
+
+	target_uri = brasero_io_get_uri_from_path (parent, target);
+	if (!target_uri)
+		return FALSE;
+
+	/* we check for circular dependency here :
+	 * if the target is one of the parent of symlink */
+	size = strlen (target_uri);
+	if (!strncmp (target_uri, escaped_uri, size)
+	&& (*(escaped_uri + size) == '/' || *(escaped_uri + size) == '\0')) {
+		g_free (target_uri);
+		return FALSE;
+	}
+
+	g_file_info_set_symlink_target (info, target_uri);
+	g_free (target_uri);
+
+	return TRUE;
+}
+
+/**
+ * Used to retrieve metadata for audio files
+ */
+
+struct _BraserIOMetadataTask {
+	gchar *uri;
+	GSList *results;
+};
+typedef struct _BraseroIOMetadataTask BraseroIOMetadataTask;
+
+static gint
+brasero_io_metadata_lookup_buffer (gconstpointer a, gconstpointer b)
+{
+	const BraseroMetadataInfo *metadata = a;
+	const gchar *uri = b;
+
+	return strcmp (uri, metadata->uri);
+}
+
+static void
+brasero_io_set_metadata_attributes (GFileInfo *info,
+				    BraseroMetadataInfo *metadata)
+{
+	g_file_info_set_attribute_int32 (info, BRASERO_IO_ISRC, metadata->isrc);
+	g_file_info_set_attribute_uint64 (info, BRASERO_IO_LEN, metadata->len);
+
+	if (metadata->artist)
+		g_file_info_set_attribute_string (info, BRASERO_IO_ARTIST, metadata->artist);
+
+	if (metadata->title)
+		g_file_info_set_attribute_string (info, BRASERO_IO_TITLE, metadata->title);
+
+	if (metadata->album)
+		g_file_info_set_attribute_string (info, BRASERO_IO_ALBUM, metadata->album);
+
+	if (metadata->genre)
+		g_file_info_set_attribute_string (info, BRASERO_IO_GENRE, metadata->genre);
+
+	if (metadata->composer)
+		g_file_info_set_attribute_string (info, BRASERO_IO_COMPOSER, metadata->composer);
+
+	g_file_info_set_attribute_boolean (info, BRASERO_IO_HAS_AUDIO, metadata->has_audio);
+	g_file_info_set_attribute_boolean (info, BRASERO_IO_HAS_VIDEO, metadata->has_video);
+	g_file_info_set_attribute_boolean (info, BRASERO_IO_IS_SEEKABLE, metadata->is_seekable);
+
+	/* FIXME: what about silences */
+}
+
+static gboolean
+brasero_io_get_metadata_info (BraseroIO *self,
+			      GCancellable *cancel,
+			      const gchar *uri,
+			      GFileInfo *info,
+			      BraseroMetadataFlag flags,
+			      BraseroMetadataInfo *meta_info)
+{
+	BraseroMetadata *metadata = NULL;
+	BraseroIOPrivate *priv;
+	const gchar *mime;
+	gboolean result;
+	GList *node;
+
+	priv = BRASERO_IO_PRIVATE (self);
+
+	mime = g_file_info_get_content_type (info);
+	if (mime
+	&& (!strncmp (mime, "image/", 6)
+	||  !strcmp (mime, "text/plain")
+	||  !strcmp (mime, "application/x-cue") /* this one make gstreamer crash */
+	||  !strcmp (mime, "application/x-cd-image")
+	||  !strcmp (mime, "application/octet-stream")))
+		return FALSE;
+
+	/* seek in the buffer if we have already explored these metadata */
+	node = g_queue_find_custom (priv->meta_buffer,
+				    uri,
+				    brasero_io_metadata_lookup_buffer);
+	if (node) {
+		brasero_metadata_info_copy (meta_info, node->data);
+		return TRUE;
+	}
+
+	/* grab an available metadata (NOTE: there should always be at least one
+	 * since we run 2 threads at max and have two metadatas available) */
+	do {
+		g_mutex_lock (priv->lock);
+		if (priv->metadatas) {
+			metadata = priv->metadatas->data;
+			priv->metadatas = g_slist_remove (priv->metadatas, metadata);
+		}
+		g_mutex_unlock (priv->lock);
+
+		g_usleep (250);
+	} while (!metadata);
+
+	result = brasero_metadata_get_info_wait (metadata,
+						 cancel,
+						 uri,
+						 flags,
+						 NULL);
+	brasero_metadata_set_info (metadata, meta_info);
+
+	if (result) {
+		/* see if we should add it to the buffer */
+		if (meta_info->has_audio || meta_info->has_video) {
+			BraseroMetadataInfo *copy;
+
+			copy = g_new0 (BraseroMetadataInfo, 1);
+			brasero_metadata_set_info (metadata, copy);
+			g_queue_push_head (priv->meta_buffer, copy);
+			if (g_queue_get_length (priv->meta_buffer) > MAX_BUFFERED_META) {
+				meta_info = g_queue_pop_tail (priv->meta_buffer);
+				brasero_metadata_info_free (meta_info);
+			}
+		}
+	}
+
+	g_mutex_lock (priv->lock);
+	priv->metadatas = g_slist_prepend (priv->metadatas, metadata);
+	g_mutex_unlock (priv->lock);
+
+	return result;
+}
+
+/**
+ * Used to get information about files
+ */
+
+static BraseroAsyncTaskResult
+brasero_io_get_file_info_thread (BraseroAsyncTaskManager *manager,
+				 GCancellable *cancel,
+				 gpointer callback_data)
+{
+	gchar attributes [256] = {G_FILE_ATTRIBUTE_STANDARD_NAME ","
+				  G_FILE_ATTRIBUTE_STANDARD_SIZE ","
+				  G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
+				  G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
+				  G_FILE_ATTRIBUTE_STANDARD_TYPE};
+	BraseroIOJob *job = callback_data;
+	gchar *file_uri = NULL;
+	GError *error = NULL;
+	GFileInfo *info;
+	GFile *file;
+
+	if (job->options & BRASERO_IO_INFO_CHECK_PARENT_SYMLINK) {
+		/* If we want to make sure a directory is not added twice we have to make sure
+		 * that it doesn't have a symlink as parent otherwise "/home/Foo/Bar" with Foo
+		 * as a symlink pointing to /tmp would be seen as a different file from /tmp/Bar 
+		 * It would be much better if we could use the inode numbers provided by gnome_vfs
+		 * unfortunately they are guint64 and can't be used in hash tables as keys.
+		 * Therefore we check parents up to root to see if there are symlinks and if so
+		 * we get a path without symlinks in it. This is done only for local file */
+		file_uri = brasero_io_check_for_parent_symlink (job->uri, cancel);
+	}
+
+	if (g_cancellable_is_cancelled (cancel)) {
+		g_free (file_uri);
+		return BRASERO_ASYNC_TASK_FINISHED;
+	}
+	
+	if (job->options & BRASERO_IO_INFO_PERM)
+		strcat (attributes, "," G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
+	if (job->options & BRASERO_IO_INFO_MIME)
+		strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
+
+	file = g_file_new_for_uri (file_uri?file_uri:job->uri);
+	info = g_file_query_info (file,
+				  attributes,
+				  G_FILE_QUERY_INFO_NONE,	/* follow symlinks */
+				  cancel,
+				  &error);
+
+	if (error) {
+		brasero_io_return_result (BRASERO_IO (manager),
+					  job->base,
+					  file_uri?file_uri:job->uri,
+					  NULL,
+					  error,
+					  job->callback_data);
+		g_free (file_uri);
+		g_object_unref (file);
+		return BRASERO_ASYNC_TASK_FINISHED;
+	}
+
+	if (g_file_info_get_is_symlink (info)) {
+		GFile *parent;
+
+		parent = g_file_get_parent (file);
+		if (!brasero_io_check_symlink_target (parent, info, file_uri?file_uri:job->uri)) {
+			error = g_error_new (BRASERO_ERROR,
+					     BRASERO_ERROR_SYMLINK_LOOP,
+					     _("recursive symbolic link"));
+
+			/* since we checked for the existence of the file
+			 * an error means a looping symbolic link */
+			brasero_io_return_result (BRASERO_IO (manager),
+						  job->base,
+						  file_uri?file_uri:job->uri,
+						  NULL,
+						  error,
+						  job->callback_data);
+			g_free (file_uri);
+			g_object_unref (info);
+			g_object_unref (file);
+			g_object_unref (parent);
+			return BRASERO_ASYNC_TASK_FINISHED;
+		}
+		g_object_unref (parent);
+	}
+	g_object_unref (file);
+
+	/* see if we are supposed to get metadata for this file (provided it's
+	 * an audio file of course). */
+	if (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY
+	&&  job->options & BRASERO_IO_INFO_METADATA) {
+		BraseroMetadataInfo metadata = { NULL };
+		gboolean result;
+
+		result = brasero_io_get_metadata_info (BRASERO_IO (manager),
+						       cancel,
+						       file_uri?file_uri:job->uri,
+						       info,
+						       BRASERO_METADATA_FLAG_NONE,
+						       &metadata);
+
+		if (result)
+			brasero_io_set_metadata_attributes (info, &metadata);
+
+		brasero_metadata_info_clear (&metadata);
+	}
+
+	brasero_io_return_result (BRASERO_IO (manager),
+				  job->base,
+				  file_uri?file_uri:job->uri,
+				  info,
+				  NULL,
+				  job->callback_data);
+	g_free (file_uri);
+
+	return BRASERO_ASYNC_TASK_FINISHED;
+}
+
+static const BraseroAsyncTaskType info_type = {
+	brasero_io_get_file_info_thread,
+	brasero_io_job_destroy
+};
+
+static void
+brasero_io_new_file_info_job (BraseroIO *self,
+			      const gchar *uri,
+			      const BraseroIOJobBase *base,
+			      BraseroIOFlags options,
+			      BraseroIOResultCallbackData *callback_data)
+{
+	BraseroIOJob *job;
+
+	job = g_new0 (BraseroIOJob, 1);
+	brasero_io_set_job (job,
+			    base,
+			    uri,
+			    options,
+			    callback_data);
+
+	brasero_io_push_job (self, job, &info_type);
+}
+
+void
+brasero_io_get_file_info (BraseroIO *self,
+			  const gchar *uri,
+			  const BraseroIOJobBase *base,
+			  BraseroIOFlags options,
+			  gpointer user_data)
+{
+	BraseroIOResultCallbackData *callback_data = NULL;
+
+	if (user_data) {
+		callback_data = g_new0 (BraseroIOResultCallbackData, 1);
+		callback_data->callback_data = user_data;
+	}
+
+	brasero_io_new_file_info_job (self, uri, base, options, callback_data);
+}
+
+/**
+ * Used to parse playlists
+ */
+
+#ifdef BUILD_PLAYLIST
+
+struct _BraseroIOPlaylist {
+	gchar *title;
+	GSList *uris;
+};
+typedef struct _BraseroIOPlaylist BraseroIOPlaylist;
+
+static void
+brasero_io_playlist_clear (BraseroIOPlaylist *data)
+{
+	g_slist_foreach (data->uris, (GFunc) g_free, NULL);
+	g_slist_free (data->uris);
+
+	g_free (data->title);
+}
+
+static void
+brasero_io_add_playlist_entry_parsed_cb (TotemPlParser *parser,
+					 const gchar *uri,
+					 GHashTable *metadata,
+					 BraseroIOPlaylist *data)
+{
+	data->uris = g_slist_prepend (data->uris, g_strdup (uri));
+}
+
+static void
+brasero_io_start_end_playlist_cb (TotemPlParser *parser,
+				  const gchar *title,
+				  BraseroIOPlaylist *data)
+{
+	if (!title)
+		return;
+
+	if (!data->title)
+		data->title = g_strdup (title);
+}
+
+static gboolean
+brasero_io_parse_playlist_get_uris (const gchar *uri,
+				    BraseroIOPlaylist *playlist,
+				    GError **error)
+{
+	gboolean result;
+	TotemPlParser *parser;
+
+	parser = totem_pl_parser_new ();
+	g_signal_connect (parser,
+			  "playlist-start",
+			  G_CALLBACK (brasero_io_start_end_playlist_cb),
+			  playlist);
+	g_signal_connect (parser,
+			  "playlist-end",
+			  G_CALLBACK (brasero_io_start_end_playlist_cb),
+			  playlist);
+	g_signal_connect (parser,
+			  "entry-parsed",
+			  G_CALLBACK (brasero_io_add_playlist_entry_parsed_cb),
+			  playlist);
+
+	if (g_object_class_find_property (G_OBJECT_GET_CLASS (parser), "recurse"))
+		g_object_set (G_OBJECT (parser), "recurse", FALSE, NULL);
+
+	result = totem_pl_parser_parse (parser, uri, TRUE);
+	g_object_unref (parser);
+
+	if (!result) {
+		g_set_error (error,
+			     BRASERO_ERROR,
+			     BRASERO_ERROR_GENERAL,
+			     _("the file doesn't appear to be a playlist"));
+
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static BraseroAsyncTaskResult
+brasero_io_parse_playlist_thread (BraseroAsyncTaskManager *manager,
+				  GCancellable *cancel,
+				  gpointer callback_data)
+{
+	GSList *iter;
+	gboolean result;
+	GFileInfo *info;
+	GError *error = NULL;
+	BraseroIOJob *job = callback_data;
+	BraseroIOPlaylist data = { NULL, };
+
+	result = brasero_io_parse_playlist_get_uris (job->uri, &data, &error);
+	if (!result) {
+		brasero_io_return_result (BRASERO_IO (manager),
+					  job->base,
+					  job->uri,
+					  NULL,
+					  error,
+					  job->callback_data);
+
+		return BRASERO_ASYNC_TASK_FINISHED;
+	}
+
+	if (g_cancellable_is_cancelled (cancel))
+		return BRASERO_ASYNC_TASK_FINISHED;
+
+	/* that's finished; Send the title */
+	info = g_file_info_new ();
+	g_file_info_set_attribute_string (info, BRASERO_IO_PLAYLIST_TITLE, data.title ? data.title:_("No title"));
+	brasero_io_return_result (BRASERO_IO (manager),
+				  job->base,
+				  job->uri,
+				  info,
+				  NULL,
+				  job->callback_data);
+
+	/* Now get information about each file in the list */
+	for (iter = data.uris; iter; iter = iter->next) {
+		gchar *child;
+
+		child = iter->data;
+		brasero_io_new_file_info_job (BRASERO_IO (manager),
+					      child,
+					      job->base,
+					      job->options,
+					      job->callback_data);
+	}
+
+	brasero_io_playlist_clear (&data);
+	return BRASERO_ASYNC_TASK_FINISHED;
+}
+
+static const BraseroAsyncTaskType playlist_type = {
+	brasero_io_parse_playlist_thread,
+	brasero_io_job_destroy
+};
+
+void
+brasero_io_parse_playlist (BraseroIO *self,
+			   const gchar *uri,
+			   const BraseroIOJobBase *base,
+			   BraseroIOFlags options,
+			   gpointer user_data)
+{
+	BraseroIOJob *job;
+	BraseroIOResultCallbackData *callback_data = NULL;
+
+	if (user_data) {
+		callback_data = g_new0 (BraseroIOResultCallbackData, 1);
+		callback_data->callback_data = user_data;
+	}
+
+	job = g_new0 (BraseroIOJob, 1);
+	brasero_io_set_job (job,
+			    base,
+			    uri,
+			    options,
+			    callback_data);
+
+	brasero_io_push_job (self, job, &playlist_type);
+}
+
+#endif
+
+/**
+ * Used to count the number of files under a directory and the children size
+ */
+
+struct _BraseroIOCountData {
+	BraseroIOJob job;
+
+	GSList *uris;
+	GSList *children;
+
+	guint files_num;
+	guint files_invalid;
+
+	guint64 total_b;
+	gboolean progress_started;
+};
+typedef struct _BraseroIOCountData BraseroIOCountData;
+
+static void
+brasero_io_get_file_count_destroy (BraseroAsyncTaskManager *manager,
+				   gpointer callback_data)
+{
+	BraseroIOCountData *data = callback_data;
+
+	g_slist_foreach (data->uris, (GFunc) g_free, NULL);
+	g_slist_free (data->uris);
+
+	g_slist_foreach (data->children, (GFunc) g_object_unref, NULL);
+	g_slist_free (data->children);
+
+	brasero_io_job_progress_report_stop (BRASERO_IO (manager), callback_data);
+
+	brasero_io_job_free (callback_data);
+}
+
+static gboolean
+brasero_io_get_file_count_process_playlist (BraseroIO *self,
+					    GCancellable *cancel,
+					    BraseroIOCountData *data,
+					    const gchar *uri)
+{
+	BraseroIOPlaylist playlist = {NULL, };
+	GSList *iter;
+
+	if (!brasero_io_parse_playlist_get_uris (uri, &playlist, NULL))
+		return FALSE;
+
+	for (iter = playlist.uris; iter; iter = iter->next) {
+		gboolean result;
+		GFileInfo *info;
+		gchar *child_uri;
+		BraseroMetadataInfo metadata = { NULL, };
+
+		child_uri = iter->data;
+		data->files_num ++;
+
+		info = g_file_info_new ();
+		result = brasero_io_get_metadata_info (self,
+						       cancel,
+						       child_uri,
+						       info,
+						       BRASERO_METADATA_FLAG_FAST,
+						       &metadata);
+
+		if (result)
+			data->total_b += metadata.len;
+		else
+			data->files_invalid ++;
+
+		brasero_metadata_info_clear (&metadata);
+		g_object_unref (info);
+	}
+
+	brasero_io_playlist_clear (&playlist);
+	return TRUE;
+}
+
+static void
+brasero_io_get_file_count_process_file (BraseroIO *self,
+					GCancellable *cancel,
+					BraseroIOCountData *data,
+					GFile *file,
+					GFileInfo *info)
+{
+	if (data->job.options & BRASERO_IO_INFO_METADATA) {
+		BraseroMetadataInfo metadata = { NULL, };
+		gboolean result = FALSE;
+		gchar *child_uri;
+
+		child_uri = g_file_get_uri (file);
+		result = brasero_io_get_metadata_info (self,
+						       cancel,
+						       child_uri,
+						       info,
+						       BRASERO_METADATA_FLAG_NONE,
+						       &metadata);
+
+		if (result)
+			data->total_b += metadata.len;
+		/* see if that's a playlist (and if we have recursive on). */
+		else if (data->job.options & BRASERO_IO_INFO_RECURSIVE) {
+			const gchar *mime;
+
+			mime = g_file_info_get_content_type (info);
+			if (mime
+			&& (!strcmp (mime, "audio/x-scpls")
+			||  !strcmp (mime, "audio/x-ms-asx")
+			||  !strcmp (mime, "audio/x-mp3-playlist")
+			||  !strcmp (mime, "audio/x-mpegurl"))) {
+				if (!brasero_io_get_file_count_process_playlist (self, cancel, data, child_uri))
+					data->files_invalid ++;
+			}
+
+			data->files_invalid ++;
+		}
+		else
+			data->files_invalid ++;
+
+		brasero_metadata_info_clear (&metadata);
+		g_free (child_uri);
+		return;
+	}
+
+	data->total_b += g_file_info_get_size (info);
+}
+
+static void
+brasero_io_get_file_count_process_directory (BraseroIO *self,
+					     GCancellable *cancel,
+					     BraseroIOCountData *data)
+{
+	GFile *file;
+	GFileInfo *info;
+	GError *error = NULL;
+	GFileEnumerator *enumerator;
+	gchar attributes [512] = {G_FILE_ATTRIBUTE_STANDARD_NAME "," 
+				  G_FILE_ATTRIBUTE_STANDARD_SIZE "," 
+				  G_FILE_ATTRIBUTE_STANDARD_TYPE };
+
+	if ((data->job.options & BRASERO_IO_INFO_METADATA)
+	&&  (data->job.options & BRASERO_IO_INFO_RECURSIVE))
+		strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
+
+	file = data->children->data;
+	data->children = g_slist_remove (data->children, file);
+
+	enumerator = g_file_enumerate_children (file,
+						attributes,
+						G_FILE_QUERY_INFO_NONE,	/* follow symlinks */
+						cancel,
+						NULL);
+	if (!enumerator) {
+		g_object_unref (file);
+		return;
+	}
+
+	while ((info = g_file_enumerator_next_file (enumerator, cancel, &error)) || error) {
+		GFile *child;
+
+		data->files_num ++;
+
+		if (error) {
+			g_error_free (error);
+			error = NULL;
+
+			data->files_invalid ++;
+			continue;
+		}
+
+		child = g_file_get_child (file, g_file_info_get_name (info));
+
+		if (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY) {
+			brasero_io_get_file_count_process_file (self, cancel, data, child, info);
+			g_object_unref (child);
+		}
+		else
+			data->children = g_slist_prepend (data->children, child);
+
+		g_object_unref (info);
+	}
+
+	g_file_enumerator_close (enumerator, cancel, NULL);
+	g_object_unref (enumerator);
+	g_object_unref (file);
+}
+
+static gboolean
+brasero_io_get_file_count_start (BraseroIO *self,
+				 GCancellable *cancel,
+				 BraseroIOCountData *data,
+				 const gchar *uri)
+{
+	GFile *file;
+	GFileInfo *info;
+	gchar attributes [512] = {G_FILE_ATTRIBUTE_STANDARD_NAME "," 
+				  G_FILE_ATTRIBUTE_STANDARD_SIZE "," 
+				  G_FILE_ATTRIBUTE_STANDARD_TYPE };
+
+	if ((data->job.options & BRASERO_IO_INFO_METADATA)
+	&&  (data->job.options & BRASERO_IO_INFO_RECURSIVE))
+		strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
+
+	file = g_file_new_for_uri (uri);
+	info = g_file_query_info (file,
+				  attributes,
+				  G_FILE_QUERY_INFO_NONE, /* follow symlinks */
+				  cancel,
+				  NULL);
+
+	data->files_num ++;
+	if (!info) {
+		g_object_unref (file);
+		data->files_invalid ++;
+		return FALSE;
+	}
+
+	if (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY) {
+		brasero_io_get_file_count_process_file (self, cancel, data, file, info);
+		g_object_unref (file);
+	}
+	else if (data->job.options & BRASERO_IO_INFO_RECURSIVE)
+		data->children = g_slist_prepend (data->children, file);
+	else
+		g_object_unref (file);
+
+	g_object_unref (info);
+	return TRUE;
+}
+
+static void
+brasero_io_get_file_count_progress_cb (BraseroIOJob *job,
+				       BraseroIOJobProgress *progress)
+{
+	BraseroIOCountData *data = (BraseroIOCountData *) job;
+
+	progress->read_b = data->total_b;
+	progress->total_b = data->total_b;
+	progress->files_num = data->files_num;
+	progress->files_invalid = data->files_invalid;
+}
+
+static BraseroAsyncTaskResult
+brasero_io_get_file_count_thread (BraseroAsyncTaskManager *manager,
+				  GCancellable *cancel,
+				  gpointer callback_data)
+{
+	BraseroIOCountData *data = callback_data;
+	GFileInfo *info;
+	gchar *uri;
+
+	if (data->children) {
+		brasero_io_get_file_count_process_directory (BRASERO_IO (manager), cancel, data);
+		return BRASERO_ASYNC_TASK_RESCHEDULE;
+	}
+	else if (!data->uris) {
+		info = g_file_info_new ();
+
+		/* set GFileInfo information */
+		g_file_info_set_attribute_uint32 (info, BRASERO_IO_COUNT_INVALID, data->files_invalid);
+		g_file_info_set_attribute_uint64 (info, BRASERO_IO_COUNT_SIZE, data->total_b);
+		g_file_info_set_attribute_uint32 (info, BRASERO_IO_COUNT_NUM, data->files_num);
+
+		brasero_io_return_result (BRASERO_IO (manager),
+					  data->job.base,
+					  NULL,
+					  info,
+					  NULL,
+					  data->job.callback_data);
+
+		return BRASERO_ASYNC_TASK_FINISHED;
+	}
+
+	if (!data->progress_started) {
+		brasero_io_job_progress_report_start (BRASERO_IO (manager),
+						      &data->job,
+						      brasero_io_get_file_count_progress_cb);
+		data->progress_started = 1;
+	}
+
+	uri = data->uris->data;
+	data->uris = g_slist_remove (data->uris, uri);
+
+	brasero_io_get_file_count_start (BRASERO_IO (manager), cancel, data, uri);
+	g_free (uri);
+
+	return BRASERO_ASYNC_TASK_RESCHEDULE;
+}
+
+static const BraseroAsyncTaskType count_type = {
+	brasero_io_get_file_count_thread,
+	brasero_io_get_file_count_destroy
+};
+
+void
+brasero_io_get_file_count (BraseroIO *self,
+			   GSList *uris,
+			   const BraseroIOJobBase *base,
+			   BraseroIOFlags options,
+			   gpointer user_data)
+{
+	BraseroIOCountData *data;
+	BraseroIOResultCallbackData *callback_data = NULL;
+
+	if (user_data) {
+		callback_data = g_new0 (BraseroIOResultCallbackData, 1);
+		callback_data->callback_data = user_data;
+	}
+
+	data = g_new0 (BraseroIOCountData, 1);
+
+	for (; uris; uris = uris->next)
+		data->uris = g_slist_prepend (data->uris, g_strdup (uris->data));
+
+	brasero_io_set_job (BRASERO_IO_JOB (data),
+			    base,
+			    NULL,
+			    options,
+			    callback_data);
+
+	brasero_io_push_job (self, BRASERO_IO_JOB (data), &count_type);
+}
+
+/**
+ * Used to explore directories
+ */
+
+struct _BraseroIOContentsData {
+	BraseroIOJob job;
+	GSList *children;
+};
+typedef struct _BraseroIOContentsData BraseroIOContentsData;
+
+static void
+brasero_io_load_directory_destroy (BraseroAsyncTaskManager *manager,
+				   gpointer callback_data)
+{
+	BraseroIOContentsData *data = callback_data;
+
+	g_slist_foreach (data->children, (GFunc) g_object_unref, NULL);
+	g_slist_free (data->children);
+
+	brasero_io_job_free (BRASERO_IO_JOB (data));
+}
+
+static gboolean
+brasero_io_load_directory_playlist (BraseroIO *self,
+				    GCancellable *cancel,
+				    BraseroIOContentsData *data,
+				    const gchar *uri,
+				    const gchar *attributes)
+{
+	BraseroIOPlaylist playlist = {NULL, };
+	GSList *iter;
+
+	if (!brasero_io_parse_playlist_get_uris (uri, &playlist, NULL))
+		return FALSE;
+
+	for (iter = playlist.uris; iter; iter = iter->next) {
+		GFile *file;
+		gboolean result;
+		GFileInfo *info;
+		gchar *child_uri;
+		BraseroMetadataInfo metadata = { NULL, };
+
+		child_uri = iter->data;
+
+		file = g_file_new_for_uri (child_uri);
+		info = g_file_query_info (file,
+					  attributes,
+					  G_FILE_QUERY_INFO_NONE,		/* follow symlinks */
+					  cancel,
+					  NULL);
+		if (!info) {
+			g_object_unref (file);
+			continue;
+		}
+
+		result = brasero_io_get_metadata_info (self,
+						       cancel,
+						       child_uri,
+						       info,
+						       BRASERO_METADATA_FLAG_FAST,
+						       &metadata);
+
+		if (result) {
+			brasero_io_set_metadata_attributes (info, &metadata);
+			brasero_io_return_result (self,
+						  data->job.base,
+						  child_uri,
+						  info,
+						  NULL,
+						  data->job.callback_data);
+		}
+		else
+			g_object_unref (info);
+
+		brasero_metadata_info_clear (&metadata);
+
+		g_object_unref (file);
+	}
+
+	brasero_io_playlist_clear (&playlist);
+	return TRUE;
+}
+
+static BraseroAsyncTaskResult
+brasero_io_load_directory_thread (BraseroAsyncTaskManager *manager,
+				  GCancellable *cancel,
+				  gpointer callback_data)
+{
+	gchar attributes [512] = {G_FILE_ATTRIBUTE_STANDARD_NAME "," 
+				  G_FILE_ATTRIBUTE_STANDARD_SIZE ","
+				  G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
+				  G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
+				  G_FILE_ATTRIBUTE_STANDARD_TYPE };
+	BraseroIOContentsData *data = callback_data;
+	GFileEnumerator *enumerator;
+	GError *error = NULL;
+	GFileInfo *info;
+	GFile *file;
+
+	if (data->job.options & BRASERO_IO_INFO_PERM)
+		strcat (attributes, "," G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
+
+	if (data->job.options & BRASERO_IO_INFO_MIME)
+		strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
+	else if ((data->job.options & BRASERO_IO_INFO_METADATA)
+	     &&  (data->job.options & BRASERO_IO_INFO_RECURSIVE))
+		strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
+
+	if (data->children) {
+		file = data->children->data;
+		data->children = g_slist_remove (data->children, file);
+	}
+	else
+		file = g_file_new_for_uri (data->job.uri);
+
+	enumerator = g_file_enumerate_children (file,
+						attributes,
+						G_FILE_QUERY_INFO_NONE,		/* follow symlinks */
+						cancel,
+						&error);
+
+	if (!enumerator) {
+		gchar *directory_uri;
+
+		directory_uri = g_file_get_uri (file);
+		brasero_io_return_result (BRASERO_IO (manager),
+					  data->job.base,
+					  directory_uri,
+					  NULL,
+					  error,
+					  data->job.callback_data);
+		g_free (directory_uri);
+		g_object_unref (file);
+
+		if (data->children)
+			return BRASERO_ASYNC_TASK_RESCHEDULE;
+
+		return BRASERO_ASYNC_TASK_FINISHED;
+	}
+
+	while ((info = g_file_enumerator_next_file (enumerator, cancel, NULL))) {
+		const gchar *name;
+		gchar *child_uri;
+		GFile *child;
+
+		name = g_file_info_get_name (info);
+		if (g_cancellable_is_cancelled (cancel)) {
+			g_object_unref (info);
+			break;
+		}
+
+		if (name [0] == '.'
+		&& (name [1] == '\0'
+		|| (name [1] == '.' && name [2] == '\0'))) {
+			g_object_unref (info);
+			continue;
+		}
+
+		child = g_file_get_child (file, name);
+		child_uri = g_file_get_uri (child);
+
+		/* special case for symlinks */
+		if (g_file_info_get_is_symlink (info)) {
+			if (!brasero_io_check_symlink_target (file, info, child_uri)) {
+				error = g_error_new (BRASERO_ERROR,
+						     BRASERO_ERROR_SYMLINK_LOOP,
+						     _("recursive symbolic link"));
+
+				/* since we checked for the existence of the file
+				 * an error means a looping symbolic link */
+				brasero_io_return_result (BRASERO_IO (manager),
+							  data->job.base,
+							  child_uri,
+							  NULL,
+							  error,
+							  data->job.callback_data);
+
+				g_free (child_uri);
+				g_object_unref (info);
+				g_object_unref (child);
+				continue;
+			}
+		}
+
+		if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
+			brasero_io_return_result (BRASERO_IO (manager),
+						  data->job.base,
+						  child_uri,
+						  info,
+						  NULL,
+						  data->job.callback_data);
+
+			if (data->job.options & BRASERO_IO_INFO_RECURSIVE)
+				data->children = g_slist_prepend (data->children, child);
+			else
+				g_object_unref (child);
+
+			g_free (child_uri);
+			continue;
+		}
+
+		if (data->job.options & BRASERO_IO_INFO_METADATA) {
+			BraseroMetadataInfo metadata = {NULL, };
+			gboolean result;
+
+			/* add metadata information to this file */
+			result = brasero_io_get_metadata_info (BRASERO_IO (manager),
+							       cancel,
+							       child_uri,
+							       info,
+							       BRASERO_METADATA_FLAG_NONE,
+							       &metadata);
+
+			if (result)
+				brasero_io_set_metadata_attributes (info, &metadata);
+			else if (data->job.options & BRASERO_IO_INFO_RECURSIVE) {
+				const gchar *mime;
+
+				mime = g_file_info_get_content_type (info);
+				if (mime
+				&& (!strcmp (mime, "audio/x-scpls")
+				||  !strcmp (mime, "audio/x-ms-asx")
+				||  !strcmp (mime, "audio/x-mp3-playlist")
+				||  !strcmp (mime, "audio/x-mpegurl")))
+					brasero_io_load_directory_playlist (BRASERO_IO (manager),
+									    cancel,
+									    data,
+									    child_uri,
+									    attributes);
+			}
+
+			brasero_metadata_info_clear (&metadata);
+		}
+
+		brasero_io_return_result (BRASERO_IO (manager),
+					  data->job.base,
+					  child_uri,
+					  info,
+					  NULL,
+					  data->job.callback_data);
+		g_free (child_uri);
+		g_object_unref (child);
+	}
+
+	if (data->job.callback_data->ref < 2) {
+		/* No result was returned so we need to return a dummy one to 
+		 * clean the callback_data in the main loop. */
+		brasero_io_return_result (BRASERO_IO (manager),
+					  data->job.base,
+					  NULL,
+					  NULL,
+					  NULL,
+					  data->job.callback_data);
+	}
+
+	g_file_enumerator_close (enumerator, NULL, NULL);
+	g_object_unref (enumerator);
+	g_object_unref (file);
+
+	if (data->children)
+		return BRASERO_ASYNC_TASK_RESCHEDULE;
+
+	return BRASERO_ASYNC_TASK_FINISHED;
+}
+
+static const BraseroAsyncTaskType contents_type = {
+	brasero_io_load_directory_thread,
+	brasero_io_load_directory_destroy
+};
+
+void
+brasero_io_load_directory (BraseroIO *self,
+			   const gchar *uri,
+			   const BraseroIOJobBase *base,
+			   BraseroIOFlags options,
+			   gpointer user_data)
+{
+	BraseroIOContentsData *data;
+	BraseroIOResultCallbackData *callback_data = NULL;
+
+	if (user_data) {
+		callback_data = g_new0 (BraseroIOResultCallbackData, 1);
+		callback_data->callback_data = user_data;
+	}
+
+	data = g_new0 (BraseroIOContentsData, 1);
+	brasero_io_set_job (BRASERO_IO_JOB (data),
+			    base,
+			    uri,
+			    options,
+			    callback_data);
+
+	brasero_io_push_job (self, BRASERO_IO_JOB (data), &contents_type);
+}
+
+/**
+ * That's for file transfer
+ */
+
+struct _BraseroIOXferPair {
+	GFile *src;
+	gchar *dest;
+};
+typedef struct _BraseroIOXferPair BraseroIOXferPair;
+
+struct _BraseroIOXferData {
+	BraseroIOCountData count;
+	BraseroIOJobProgress *progress;
+
+	gchar *dest_path;
+
+	guint64 current_read_b;
+	guint64 current_total_b;
+	guint64 read_b;
+
+	GFile *current;
+	GMutex *current_lock;
+
+	GFileInfo *info;
+	GSList *pairs;
+};
+typedef struct _BraseroIOXferData BraseroIOXferData;
+
+static void
+brasero_io_xfer_pair_free (BraseroIOXferPair *pair)
+{
+	g_object_unref (pair->src);
+	g_free (pair->dest);
+
+	g_free (pair);
+}
+
+static void
+brasero_io_xfer_destroy (BraseroAsyncTaskManager *manager,
+			 gpointer callback_data)
+{
+	BraseroIOXferData *data = callback_data;
+
+	g_slist_foreach (data->pairs, (GFunc) brasero_io_xfer_pair_free, NULL);
+	g_slist_free (data->pairs);
+	g_free (data->dest_path);
+
+	g_mutex_free (data->current_lock);
+
+	/* no need to stop progress report as the following function will do it */
+	brasero_io_get_file_count_destroy (manager, callback_data);
+}
+
+static void
+brasero_io_xfer_progress_cb (goffset current_num_bytes,
+			     goffset total_num_bytes,
+			     gpointer callback_data)
+{
+	BraseroIOXferData *data = callback_data;
+
+	data->current_read_b = current_num_bytes;
+	data->current_total_b = total_num_bytes;
+}
+
+static BraseroAsyncTaskResult
+brasero_io_xfer_file_thread (BraseroIOXferData *data,
+			     GCancellable *cancel,
+			     GFile *src,
+			     const gchar *dest_path,
+			     GError **error)
+{
+	GFile *dest;
+	gboolean result;
+
+	g_mutex_lock (data->current_lock);
+	data->current = src;
+	g_mutex_unlock (data->current_lock);
+
+	dest = g_file_new_for_path (dest_path);
+	result = g_file_copy (src,
+			      dest,
+			      G_FILE_COPY_ALL_METADATA,
+			      cancel,
+			      brasero_io_xfer_progress_cb,
+			      data,
+			      error);
+	g_object_unref (dest);
+
+	data->read_b += data->current_total_b;
+	data->current_read_b = 0;
+
+	g_mutex_lock (data->current_lock);
+	data->current = NULL;
+	g_mutex_unlock (data->current_lock);
+
+	return result;
+}
+
+static gboolean
+brasero_io_xfer_recursive_thread (BraseroIOXferData *data,
+				  GCancellable *cancel,
+				  GFile *src,
+				  const gchar *dest_path,
+				  GError **error)
+{
+	GFileInfo *info;
+	GFileEnumerator *enumerator;
+
+	enumerator = g_file_enumerate_children (src,
+						G_FILE_ATTRIBUTE_STANDARD_TYPE ","
+						G_FILE_ATTRIBUTE_STANDARD_NAME,
+						G_FILE_QUERY_INFO_NONE,	/* follow symlinks */
+						cancel,
+						error);
+	if (!enumerator)
+		return FALSE;
+
+	while ((info = g_file_enumerator_next_file (enumerator, cancel, NULL))) {
+		gboolean result;
+		GFile *src_child;
+		gchar *dest_child;
+
+		if (g_cancellable_is_cancelled (cancel)) {
+			result = FALSE;
+			break;
+		}
+
+		if (!info)
+			continue;
+
+		src_child = g_file_get_child (src, g_file_info_get_name (info));
+		dest_child = g_build_path (G_DIR_SEPARATOR_S,
+					   dest_path,
+					   g_file_info_get_name (info),
+					   NULL);
+
+		if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
+			/* Create a directory with the same name and keep it for
+			 * later.
+			 * Note: if that fails don't bother. */
+			if (!g_mkdir (dest_child, 700)) {
+				BraseroIOXferPair *new_pair;
+
+				new_pair = g_new0 (BraseroIOXferPair, 1);
+				new_pair->src = src_child;
+				new_pair->dest = dest_child;
+				data->pairs = g_slist_prepend (data->pairs, new_pair);
+			}
+		}
+		else {
+			result = brasero_io_xfer_file_thread (data,
+							      cancel,
+							      src_child,
+							      dest_child,
+							      NULL);
+
+			g_free (dest_child);
+			g_object_unref (src_child);
+		}
+
+		g_object_unref (info);
+	}
+
+	g_file_enumerator_close (enumerator, cancel, NULL);
+	g_object_unref (enumerator);
+
+	return TRUE;
+}
+
+static gboolean
+brasero_io_xfer_start (BraseroIO *self,
+		       GCancellable *cancel,
+		       BraseroIOXferData *data,
+		       GError **error)
+{
+	GFile *file;
+	gboolean result;
+
+	/* retrieve some information about the file we have to copy */
+	file = g_file_new_for_uri (data->count.job.uri);
+	data->info = g_file_query_info (file,
+					G_FILE_ATTRIBUTE_STANDARD_TYPE","
+					G_FILE_ATTRIBUTE_STANDARD_SIZE,
+					G_FILE_QUERY_INFO_NONE, /* follow symlinks */
+					cancel,
+					error);
+	if (!data->info || error) {
+		g_object_unref (file);
+		return FALSE;
+	}
+
+	g_file_info_set_attribute_string (data->info,
+					  BRASERO_IO_XFER_DESTINATION,
+					  data->dest_path);
+
+	/* see if we should explore it beforehand to report progress */
+	if (data->count.job.base->progress) {
+		data->count.files_num = 1;
+		if (g_file_info_get_file_type (data->info) != G_FILE_TYPE_DIRECTORY)
+			brasero_io_get_file_count_process_file (self, cancel, &data->count, file, data->info);
+		else
+			brasero_io_get_file_count_process_directory (self, cancel, &data->count);
+	}
+
+	/* start the downloading */
+	if (g_file_info_get_file_type (data->info) == G_FILE_TYPE_DIRECTORY) {
+		if (g_mkdir_with_parents (data->dest_path, 700)) {
+			g_object_unref (file);
+
+			g_set_error (error,
+				     BRASERO_ERROR,
+				     BRASERO_ERROR_GENERAL,
+				     _("a directory couldn't be created (%s)"),
+				     strerror (errno));
+			return FALSE;
+		}
+
+		if (data->count.job.options & BRASERO_IO_INFO_RECURSIVE)
+			brasero_io_xfer_recursive_thread (data,
+							  cancel,
+							  file,
+							  data->dest_path,
+							  NULL);
+	}
+	else
+		result = brasero_io_xfer_file_thread (data,
+						      cancel,
+						      file,
+						      data->dest_path,
+						      error);
+
+	g_object_unref (file);
+	return result;
+}
+
+static void
+brasero_io_xfer_get_progress_cb (BraseroIOJob *job,
+				 BraseroIOJobProgress *progress)
+{
+	BraseroIOXferData *data = (BraseroIOXferData *) job;
+
+	if (progress->current)
+		g_free (progress->current);
+
+	g_mutex_lock (data->current_lock);
+	progress->current = g_file_get_basename (data->current);
+	g_mutex_unlock (data->current_lock);
+
+	progress->total_b = data->count.total_b;
+	progress->read_b = data->current_read_b + data->read_b;
+	progress->files_num = data->count.files_num - data->count.files_invalid;
+}
+
+static BraseroAsyncTaskResult
+brasero_io_xfer_thread (BraseroAsyncTaskManager *manager,
+			GCancellable *cancel,
+			gpointer callback_data)
+{
+	BraseroIOXferPair *pair;
+	BraseroIOXferData *data = callback_data;
+
+	if (!data->info) {
+		GError *error = NULL;
+
+		brasero_io_job_progress_report_start (BRASERO_IO (manager),
+						      callback_data,
+						      brasero_io_xfer_get_progress_cb);
+
+		if (!brasero_io_xfer_start (BRASERO_IO (manager), cancel, data, &error)) {
+			brasero_io_return_result (BRASERO_IO (manager),
+						  data->count.job.base,
+						  data->count.job.uri,
+						  NULL,
+						  error,
+						  data->count.job.callback_data);
+			return BRASERO_ASYNC_TASK_FINISHED;
+		}
+
+		if (data->pairs)
+			return BRASERO_ASYNC_TASK_RESCHEDULE;
+
+		brasero_io_return_result (BRASERO_IO (manager),
+					  data->count.job.base,
+					  data->count.job.uri,
+					  data->info,
+					  NULL,
+					  data->count.job.callback_data);
+		data->info = NULL;
+		return BRASERO_ASYNC_TASK_FINISHED;
+	}
+
+	/* If there is a progress callback, retrieve the size of all the data. */
+	if (data->count.children) {
+		brasero_io_get_file_count_process_directory (BRASERO_IO (manager), cancel, &data->count);
+		return BRASERO_ASYNC_TASK_RESCHEDULE;
+	}
+
+	pair = data->pairs->data;
+	data->pairs = g_slist_remove (data->pairs, pair);
+
+	brasero_io_xfer_recursive_thread (data,
+					  cancel,
+					  pair->src,
+					  pair->dest,
+					  NULL);
+
+	brasero_io_xfer_pair_free (pair);
+
+	if (data->pairs)
+		return BRASERO_ASYNC_TASK_RESCHEDULE;
+
+	brasero_io_return_result (BRASERO_IO (manager),
+				  data->count.job.base,
+				  data->count.job.uri,
+				  data->info,
+				  NULL,
+				  data->count.job.callback_data);
+	data->info = NULL;
+
+	return BRASERO_ASYNC_TASK_FINISHED;
+}
+
+static const BraseroAsyncTaskType xfer_type = {
+	brasero_io_xfer_thread,
+	brasero_io_xfer_destroy
+};
+
+void
+brasero_io_xfer (BraseroIO *self,
+		 const gchar *uri,
+		 const gchar *dest_path,
+		 const BraseroIOJobBase *base,
+		 BraseroIOFlags options,
+		 gpointer user_data)
+{
+	BraseroIOXferData *data;
+	BraseroIOResultCallbackData *callback_data = NULL;
+
+	if (user_data) {
+		callback_data = g_new0 (BraseroIOResultCallbackData, 1);
+		callback_data->callback_data = user_data;
+	}
+
+	data = g_new0 (BraseroIOXferData, 1);
+	data->dest_path = g_strdup (dest_path);
+	data->current_lock = g_mutex_new ();
+
+	brasero_io_set_job (BRASERO_IO_JOB (data),
+			    base,
+			    uri,
+			    options,
+			    callback_data);
+
+	brasero_io_push_job (self, BRASERO_IO_JOB (data), &xfer_type);
+}
+
+static void
+brasero_io_cancel_result (BraseroIO *self,
+			  BraseroIOJobResult *result)
+{
+	BraseroIOResultCallbackData *data;
+	BraseroIOPrivate *priv;
+
+	priv = BRASERO_IO_PRIVATE (self);
+
+	g_mutex_lock (priv->lock);
+	priv->results = g_slist_remove (priv->results, result);
+	g_mutex_unlock (priv->lock);
+
+	data = result->callback_data;
+	brasero_io_unref_result_callback_data (data,
+					       result->base->object,
+					       result->base->destroy,
+					       TRUE);
+	brasero_io_job_result_free (result);
+}
+
+static gboolean
+brasero_io_cancel_tasks_by_base_cb (BraseroAsyncTaskManager *manager,
+				    gpointer callback_data,
+				    gpointer user_data)
+{
+	BraseroIOJob *job = callback_data;
+	BraseroIOJobBase *base = user_data;
+
+	if (job->base != base)
+		return FALSE;
+
+	return TRUE;
+}
+
+void
+brasero_io_cancel_by_base (BraseroIO *self,
+			   BraseroIOJobBase *base)
+{
+	GSList *iter;
+	GSList *next;
+	BraseroIOPrivate *priv;
+
+	priv = BRASERO_IO_PRIVATE (self);
+
+	brasero_async_task_manager_foreach_unprocessed_remove (BRASERO_ASYNC_TASK_MANAGER (self),
+							       brasero_io_cancel_tasks_by_base_cb,
+							       base);
+
+	brasero_async_task_manager_foreach_active_remove (BRASERO_ASYNC_TASK_MANAGER (self),
+							  brasero_io_cancel_tasks_by_base_cb,
+							  base);
+
+	/* do it afterwards in case some results slipped through */
+	for (iter = priv->results; iter; iter = next) {
+		BraseroIOJobResult *result;
+
+		result = iter->data;
+		next = iter->next;
+
+		if (result->base != base)
+			continue;
+
+		brasero_io_cancel_result (self, result);
+	}
+}
+
+static gboolean
+brasero_io_cancel_tasks_by_data_cb (BraseroAsyncTaskManager *manager,
+				    gpointer callback_data,
+				    gpointer user_data)
+{
+	BraseroIOJob *job = callback_data;
+
+	if (job->callback_data != user_data)
+		return FALSE;
+
+	return TRUE;
+}
+
+void
+brasero_io_cancel_by_data (BraseroIO *self,
+			   gpointer callback_data)
+{
+	GSList *iter;
+	GSList *next;
+	BraseroIOPrivate *priv;
+
+	priv = BRASERO_IO_PRIVATE (self);
+
+	brasero_async_task_manager_foreach_unprocessed_remove (BRASERO_ASYNC_TASK_MANAGER (self),
+							       brasero_io_cancel_tasks_by_data_cb,
+							       callback_data);
+
+	brasero_async_task_manager_foreach_active_remove (BRASERO_ASYNC_TASK_MANAGER (self),
+							  brasero_io_cancel_tasks_by_data_cb,
+							  callback_data);
+
+	/* do it afterwards in case some results slipped through */
+	for (iter = priv->results; iter; iter = next) {
+		BraseroIOJobResult *result;
+
+		result = iter->data;
+		next = iter->next;
+
+		if (result->callback_data != callback_data)
+			continue;
+
+		brasero_io_cancel_result (self, result);
+	}
+}
+
+struct _BraseroIOJobCompareData {
+	BraseroIOCompareCallback func;
+	const BraseroIOJobBase *base;
+	gpointer user_data;
+};
+typedef struct _BraseroIOJobCompareData BraseroIOJobCompareData;
+
+static gboolean
+brasero_io_compare_unprocessed_task (BraseroAsyncTaskManager *manager,
+				     gpointer task,
+				     gpointer callback_data)
+{
+	BraseroIOJob *job = task;
+	BraseroIOJobCompareData *data = callback_data;
+
+	if (job->base == data->base)
+		return FALSE;
+
+	return data->func (job->callback_data, data->user_data);
+}
+
+void
+brasero_io_find_urgent (BraseroIO *self,
+			const BraseroIOJobBase *base,
+			BraseroIOCompareCallback callback,
+			gpointer user_data)
+{
+	BraseroIOJobCompareData callback_data;
+
+	callback_data.func = callback;
+	callback_data.base = base;
+	callback_data.user_data = user_data;
+
+	brasero_async_task_manager_find_urgent_task (BRASERO_ASYNC_TASK_MANAGER (self),
+						     brasero_io_compare_unprocessed_task,
+						     &callback_data);
+						     
+}
+
+BraseroIOJobBase *
+brasero_io_register (GObject *object,
+		     BraseroIOResultCallback callback,
+		     BraseroIODestroyCallback destroy,
+		     BraseroIOProgressCallback progress)
+{
+	BraseroIOJobBase *base;
+
+	base = g_new0 (BraseroIOJobBase, 1);
+	base->object = object;
+	base->callback = callback;
+	base->destroy = destroy;
+	base->progress = progress;
+
+	return base;
+}
+
+static void
+brasero_io_init (BraseroIO *object)
+{
+	BraseroIOPrivate *priv;
+	BraseroMetadata *metadata;
+	priv = BRASERO_IO_PRIVATE (object);
+
+	priv->lock = g_mutex_new ();
+
+	priv->meta_buffer = g_queue_new ();
+
+	/* create metadatas now since it doesn't work well when it's created in 
+	 * a thread. */
+	metadata = brasero_metadata_new ();
+	priv->metadatas = g_slist_prepend (priv->metadatas, metadata);
+	metadata = brasero_metadata_new ();
+	priv->metadatas = g_slist_prepend (priv->metadatas, metadata);
+}
+
+static gboolean
+brasero_io_free_async_queue (BraseroAsyncTaskManager *manager,
+			     gpointer callback_data,
+			     gpointer NULL_data)
+{
+	BraseroIOJob *job = callback_data;
+
+	if (job->base->destroy)
+		job->base->destroy (job->base->object,
+				    TRUE,
+				    job->callback_data);
+
+	brasero_io_job_free (job);
+
+	return TRUE;
+}
+
+static void
+brasero_io_finalize (GObject *object)
+{
+	BraseroIOPrivate *priv;
+	GSList *iter;
+
+	priv = BRASERO_IO_PRIVATE (object);
+
+	brasero_async_task_manager_foreach_unprocessed_remove (BRASERO_ASYNC_TASK_MANAGER (object),
+							       brasero_io_free_async_queue,
+							       NULL);
+
+	brasero_async_task_manager_foreach_active_remove (BRASERO_ASYNC_TASK_MANAGER (object),
+							  brasero_io_free_async_queue,
+							  NULL);
+
+	g_slist_foreach (priv->metadatas, (GFunc) g_object_unref, NULL);
+	g_slist_free (priv->metadatas);
+	priv->metadatas = NULL;
+
+	if (priv->meta_buffer) {
+		BraseroMetadataInfo *metadata;
+
+		while ((metadata = g_queue_pop_head (priv->meta_buffer)) != NULL)
+			brasero_metadata_info_free (metadata);
+
+		g_queue_free (priv->meta_buffer);
+		priv->meta_buffer = NULL;
+	}
+
+	if (priv->results_id) {
+		g_source_remove (priv->results_id);
+		priv->results_id = 0;
+	}
+
+	for (iter = priv->results; iter; iter = iter->next) {
+		BraseroIOJobResult *result;
+
+		result = iter->data;
+		brasero_io_job_result_free (result);
+	}
+	g_slist_free (priv->results);
+	priv->results = NULL;
+
+	if (priv->progress_id) {
+		g_source_remove (priv->progress_id);
+		priv->progress_id = 0;
+	}
+
+	if (priv->progress) {
+		g_slist_foreach (priv->progress, (GFunc) g_free, NULL);
+		g_slist_free (priv->progress);
+		priv->progress = NULL;
+	}
+
+	if (priv->lock) {
+		g_mutex_free (priv->lock);
+		priv->lock = NULL;
+	}
+
+	G_OBJECT_CLASS (brasero_io_parent_class)->finalize (object);
+}
+
+static void
+brasero_io_class_init (BraseroIOClass *klass)
+{
+	GObjectClass* object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (BraseroIOPrivate));
+
+	object_class->finalize = brasero_io_finalize;
+}
+
+static BraseroIO *singleton = NULL;
+
+static void
+brasero_io_last_reference_cb (gpointer null_data,
+			      GObject *object,
+			      gboolean is_last_ref)
+{
+	if (is_last_ref) {
+		singleton = NULL;
+		g_object_remove_toggle_ref (object,
+					    brasero_io_last_reference_cb,
+					    null_data);
+	}
+}
+
+BraseroIO *
+brasero_io_get_default ()
+{
+	if (singleton) {
+		g_object_ref (singleton);
+		return singleton;
+	}
+
+	singleton = g_object_new (BRASERO_TYPE_IO, NULL);
+	g_object_add_toggle_ref (G_OBJECT (singleton),
+				 brasero_io_last_reference_cb,
+				 NULL);
+	return singleton;
+}

Added: trunk/src/brasero-io.h
==============================================================================
--- (empty file)
+++ trunk/src/brasero-io.h	Mon Feb 25 20:02:02 2008
@@ -0,0 +1,191 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * trunk
+ * Copyright (C) Philippe Rouquier 2008 <bonfire-app wanadoo fr>
+ * 
+ * trunk is free software.
+ * 
+ * You may redistribute it and/or modify it under the terms of the
+ * GNU General Public License, as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ * 
+ * trunk is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with trunk.  If not, write to:
+ * 	The Free Software Foundation, Inc.,
+ * 	51 Franklin Street, Fifth Floor
+ * 	Boston, MA  02110-1301, USA.
+ */
+
+#ifndef _BRASERO_IO_H_
+#define _BRASERO_IO_H_
+
+#include <glib-object.h>
+
+#include "brasero-async-task-manager.h"
+
+G_BEGIN_DECLS
+
+#define BRASERO_TYPE_IO             (brasero_io_get_type ())
+#define BRASERO_IO(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), BRASERO_TYPE_IO, BraseroIO))
+#define BRASERO_IO_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), BRASERO_TYPE_IO, BraseroIOClass))
+#define BRASERO_IS_IO(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BRASERO_TYPE_IO))
+#define BRASERO_IS_IO_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), BRASERO_TYPE_IO))
+#define BRASERO_IO_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), BRASERO_TYPE_IO, BraseroIOClass))
+
+typedef struct _BraseroIOClass BraseroIOClass;
+typedef struct _BraseroIO BraseroIO;
+
+struct _BraseroIOClass
+{
+	BraseroAsyncTaskManagerClass parent_class;
+};
+
+struct _BraseroIO
+{
+	BraseroAsyncTaskManager parent_instance;
+};
+
+GType brasero_io_get_type (void) G_GNUC_CONST;
+
+typedef enum {
+	BRASERO_IO_INFO_NONE			= 0,
+	BRASERO_IO_INFO_MIME			= 1,
+	BRASERO_IO_INFO_PERM			= 1 << 1,
+	BRASERO_IO_INFO_METADATA		= 1 << 2,
+	BRASERO_IO_INFO_RECURSIVE		= 1 << 3,
+	BRASERO_IO_INFO_CHECK_PARENT_SYMLINK	= 1 << 4,
+
+	BRASERO_IO_INFO_URGENT			= 1 << 5,
+	BRASERO_IO_INFO_IDLE			= 1 << 6
+} BraseroIOFlags;
+
+typedef enum {
+	BRASERO_IO_PHASE_START		= 0,
+	BRASERO_IO_PHASE_DOWNLOAD,
+	BRASERO_IO_PHASE_END
+} BraseroIOPhase;
+
+#define BRASERO_IO_XFER_DESTINATION	"xfer::destination"
+
+#define BRASERO_IO_PLAYLIST_TITLE	"playlist::title"
+
+#define BRASERO_IO_COUNT_NUM		"count::num"
+#define BRASERO_IO_COUNT_SIZE		"count::size"
+#define BRASERO_IO_COUNT_INVALID	"count::invalid"
+
+#define BRASERO_IO_LEN			"metadata::length"
+#define BRASERO_IO_ISRC			"metadata::isrc"
+#define BRASERO_IO_TITLE		"metadata::title"
+#define BRASERO_IO_ARTIST		"metadata::artist"
+#define BRASERO_IO_ALBUM		"metadata::album"
+#define BRASERO_IO_ALBUM		"metadata::album"
+#define BRASERO_IO_GENRE		"metadata::genre"
+#define BRASERO_IO_COMPOSER		"metadata::composer"
+#define BRASERO_IO_HAS_AUDIO		"metadata::has_audio"
+#define BRASERO_IO_HAS_VIDEO		"metadata::has_video"
+#define BRASERO_IO_IS_SEEKABLE		"metadata::is_seekable"
+
+typedef struct _BraseroIOJobProgress BraseroIOJobProgress;
+
+typedef void		(*BraseroIOResultCallback)	(GObject *object,
+							 GError *error,
+							 const gchar *uri,
+							 GFileInfo *info,
+							 gpointer callback_data);
+
+typedef void		(*BraseroIOProgressCallback)	(GObject *object,
+							 BraseroIOJobProgress *info,
+							 gpointer callback_data);
+
+typedef void		(*BraseroIODestroyCallback)	(GObject *object,
+							 gboolean cancel,
+							 gpointer callback_data);
+
+typedef gboolean	(*BraseroIOCompareCallback)	(gpointer data,
+							 gpointer user_data);
+
+struct _BraseroIOJobBase {
+	GObject *object;
+	BraseroIOResultCallback callback;
+	BraseroIODestroyCallback destroy;
+	BraseroIOProgressCallback progress;
+};
+typedef struct _BraseroIOJobBase BraseroIOJobBase;
+
+
+BraseroIO *
+brasero_io_get_default (void);
+
+BraseroIOJobBase *
+brasero_io_register (GObject *object,
+		     BraseroIOResultCallback callback,
+		     BraseroIODestroyCallback destroy,
+		     BraseroIOProgressCallback progress);
+
+void
+brasero_io_cancel_by_data (BraseroIO *self,
+			   gpointer callback_data);
+
+void
+brasero_io_cancel_by_base (BraseroIO *self,
+			   BraseroIOJobBase *base);
+
+void
+brasero_io_find_urgent (BraseroIO *self,
+			const BraseroIOJobBase *base,
+			BraseroIOCompareCallback callback,
+			gpointer callback_data);			
+
+void
+brasero_io_load_directory (BraseroIO *self,
+			   const gchar *uri,
+			   const BraseroIOJobBase *base,
+			   BraseroIOFlags options,
+			   gpointer callback_data);
+void
+brasero_io_get_file_info (BraseroIO *self,
+			  const gchar *uri,
+			  const BraseroIOJobBase *base,
+			  BraseroIOFlags options,
+			  gpointer callback_data);
+void
+brasero_io_get_file_count (BraseroIO *self,
+			   GSList *uris,
+			   const BraseroIOJobBase *base,
+			   BraseroIOFlags options,
+			   gpointer callback_data);
+void
+brasero_io_parse_playlist (BraseroIO *self,
+			   const gchar *uri,
+			   const BraseroIOJobBase *base,
+			   BraseroIOFlags options,
+			   gpointer callback_data);
+void
+brasero_io_xfer (BraseroIO *self,
+		 const gchar *uri,
+		 const gchar *dest_path,
+		 const BraseroIOJobBase *base,
+		 BraseroIOFlags options,
+		 gpointer callback_data);
+
+guint64
+brasero_io_job_progress_get_read (BraseroIOJobProgress *progress);
+
+guint64
+brasero_io_job_progress_get_total (BraseroIOJobProgress *progress);
+
+BraseroIOPhase
+brasero_io_job_progress_get_phase (BraseroIOJobProgress *progress);
+
+guint
+brasero_io_job_progress_get_file_processed (BraseroIOJobProgress *progress);
+
+G_END_DECLS
+
+#endif /* _BRASERO_IO_H_ */



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