1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
4
* Copyright (C) Philippe Rouquier 2005-2008 <bonfire-app@wanadoo.fr>
6
* Brasero is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* brasero is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
* See the GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with brasero. If not, write to:
18
* The Free Software Foundation, Inc.,
19
* 51 Franklin Street, Fifth Floor
20
* Boston, MA 02110-1301, USA.
31
#include <glib-object.h>
32
#include <glib/gi18n-lib.h>
33
#include <glib/gstdio.h>
40
#include <totem-pl-parser.h>
43
#include "burn-basics.h"
44
#include "burn-debug.h"
45
#include "burn-volume.h"
47
#include "brasero-app.h"
48
#include "brasero-utils.h"
49
#include "brasero-io.h"
50
#include "brasero-metadata.h"
51
#include "brasero-async-task-manager.h"
53
typedef struct _BraseroIOPrivate BraseroIOPrivate;
54
struct _BraseroIOPrivate
60
/* used for returning results */
64
/* used for metadata */
65
GMutex *lock_metadata;
68
GSList *metadata_running;
70
/* used to "buffer" some results returned by metadata.
71
* It takes time to return metadata and it's not unusual
72
* to fetch metadata three times in a row, once for size
73
* preview, once for preview, once adding to selection */
80
#define BRASERO_IO_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), BRASERO_TYPE_IO, BraseroIOPrivate))
82
/* so far 2 metadata at a time has shown to be the best for performance */
83
#define MAX_CONCURENT_META 2
84
#define MAX_BUFFERED_META 20
86
struct _BraseroIOResultCallbackData {
87
gpointer callback_data;
90
typedef struct _BraseroIOResultCallbackData BraseroIOResultCallbackData;
92
struct _BraseroIOJob {
94
BraseroIOFlags options;
96
const BraseroIOJobBase *base;
97
BraseroIOResultCallbackData *callback_data;
99
typedef struct _BraseroIOJob BraseroIOJob;
101
#define BRASERO_IO_JOB(data) ((BraseroIOJob *) (data))
103
struct _BraseroIOJobResult {
104
const BraseroIOJobBase *base;
105
BraseroIOResultCallbackData *callback_data;
111
typedef struct _BraseroIOJobResult BraseroIOJobResult;
114
typedef void (*BraseroIOJobProgressCallback) (BraseroIOJob *job,
115
BraseroIOJobProgress *progress);
117
struct _BraseroIOJobProgress {
119
BraseroIOJobProgressCallback progress;
121
BraseroIOPhase phase;
129
guint64 current_read_b;
130
guint64 current_total_b;
135
G_DEFINE_TYPE (BraseroIO, brasero_io, BRASERO_TYPE_ASYNC_TASK_MANAGER);
139
* That's the structure to pass the progress on
143
brasero_io_job_progress_report_cb (gpointer callback_data)
145
BraseroIOPrivate *priv;
148
priv = BRASERO_IO_PRIVATE (callback_data);
150
g_mutex_lock (priv->lock);
151
for (iter = priv->progress; iter; iter = iter->next) {
152
BraseroIOJobProgress *progress;
153
gpointer callback_data;
155
progress = iter->data;
157
callback_data = progress->job->callback_data?
158
progress->job->callback_data->callback_data:
161
/* update our progress */
162
progress->progress (progress->job, progress);
163
progress->job->base->progress (progress->job->base->object,
167
g_mutex_unlock (priv->lock);
173
brasero_io_job_progress_report_start (BraseroIO *self,
175
BraseroIOJobProgressCallback callback)
177
BraseroIOJobProgress *progress;
178
BraseroIOPrivate *priv;
180
priv = BRASERO_IO_PRIVATE (self);
182
if (!job->base->progress)
185
progress = g_new0 (BraseroIOJobProgress, 1);
187
progress->progress = callback;
189
g_mutex_lock (priv->lock);
190
priv->progress = g_slist_prepend (priv->progress, progress);
191
if (!priv->progress_id)
192
priv->progress_id = g_timeout_add (500, brasero_io_job_progress_report_cb, self);
193
g_mutex_unlock (priv->lock);
197
brasero_io_job_progress_report_stop (BraseroIO *self,
200
BraseroIOPrivate *priv;
203
priv = BRASERO_IO_PRIVATE (self);
204
g_mutex_lock (priv->lock);
205
for (iter = priv->progress; iter; iter = iter->next) {
206
BraseroIOJobProgress *progress;
208
progress = iter->data;
209
if (progress->job == job) {
210
priv->progress = g_slist_remove (priv->progress, progress);
211
if (progress->current)
212
g_free (progress->current);
219
if (!priv->progress) {
220
if (priv->progress_id) {
221
g_source_remove (priv->progress_id);
222
priv->progress_id = 0;
226
g_mutex_unlock (priv->lock);
230
brasero_io_job_progress_get_current (BraseroIOJobProgress *progress)
232
return g_strdup (progress->current);
236
brasero_io_job_progress_get_file_processed (BraseroIOJobProgress *progress)
238
return progress->files_num;
242
brasero_io_job_progress_get_read (BraseroIOJobProgress *progress)
244
return progress->current_read_b + progress->read_b;
248
brasero_io_job_progress_get_total (BraseroIOJobProgress *progress)
250
return progress->total_b;
254
brasero_io_job_progress_get_phase (BraseroIOJobProgress *progress)
256
return progress->phase;
260
brasero_io_unref_result_callback_data (BraseroIOResultCallbackData *data,
262
BraseroIODestroyCallback destroy,
268
/* see atomically if we are the last to hold a lock */
269
if (!g_atomic_int_dec_and_test (&data->ref))
275
data->callback_data);
280
brasero_io_job_result_free (BraseroIOJobResult *result)
283
g_object_unref (result->info);
286
g_error_free (result->error);
289
g_free (result->uri);
295
* Used to return the results
299
brasero_io_return_result_idle (gpointer callback_data)
301
BraseroIO *self = BRASERO_IO (callback_data);
302
BraseroIOResultCallbackData *data;
303
BraseroIOJobResult *result;
304
BraseroIOPrivate *priv;
308
priv = BRASERO_IO_PRIVATE (self);
310
g_mutex_lock (priv->lock);
312
/* Put that to 0 for now so that a new idle call will be scheduled while
313
* we are in the loop. That way if we block the other one will be able
314
* to deliver the results. */
315
results_id = priv->results_id;
316
priv->results_id = 0;
318
/* Return several results at a time that can be a huge speed gain.
319
* What should be the value that provides speed and responsiveness? */
320
for (i = 0; priv->results && i < 25; i ++) {
321
result = priv->results->data;
322
priv->results = g_slist_remove (priv->results, result);
324
g_mutex_unlock (priv->lock);
326
data = result->callback_data;
327
if (result->uri || result->info || result->error)
328
result->base->callback (result->base->object,
332
data? data->callback_data:NULL);
334
/* Else this is just to call destroy () for callback data */
335
brasero_io_unref_result_callback_data (data,
336
result->base->object,
337
result->base->destroy,
339
brasero_io_job_result_free (result);
341
g_mutex_lock (priv->lock);
344
if (!priv->results_id && priv->results) {
345
/* There are still results and no idle call is scheduled so we
346
* have to restart ourselves to make sure we empty the queue */
347
priv->results_id = results_id;
348
g_mutex_unlock (priv->lock);
352
g_mutex_unlock (priv->lock);
357
brasero_io_queue_result (BraseroIO *self,
358
BraseroIOJobResult *result)
360
BraseroIOPrivate *priv;
362
priv = BRASERO_IO_PRIVATE (self);
364
/* insert the task in the results queue */
365
g_mutex_lock (priv->lock);
366
priv->results = g_slist_append (priv->results, result);
367
if (!priv->results_id)
368
priv->results_id = g_idle_add ((GSourceFunc) brasero_io_return_result_idle, self);
369
g_mutex_unlock (priv->lock);
373
brasero_io_return_result (BraseroIO *self,
374
const BraseroIOJobBase *base,
378
BraseroIOResultCallbackData *callback_data)
380
BraseroIOJobResult *result;
382
/* even if it is cancelled we let the result go through to be able to
383
* call its destroy callback in the main thread. */
385
result = g_new0 (BraseroIOJobResult, 1);
388
result->error = error;
389
result->uri = g_strdup (uri);
392
g_atomic_int_inc (&callback_data->ref);
393
result->callback_data = callback_data;
396
brasero_io_queue_result (self, result);
404
brasero_io_set_job (BraseroIOJob *job,
405
const BraseroIOJobBase *base,
407
BraseroIOFlags options,
408
BraseroIOResultCallbackData *callback_data)
411
job->uri = g_strdup (uri);
412
job->options = options;
413
job->callback_data = callback_data;
416
g_atomic_int_inc (&job->callback_data->ref);
420
brasero_io_push_job (BraseroIO *self,
422
const BraseroAsyncTaskType *type)
424
if (job->options & BRASERO_IO_INFO_URGENT)
425
brasero_async_task_manager_queue (BRASERO_ASYNC_TASK_MANAGER (self),
426
BRASERO_ASYNC_URGENT,
429
else if (job->options & BRASERO_IO_INFO_IDLE)
430
brasero_async_task_manager_queue (BRASERO_ASYNC_TASK_MANAGER (self),
435
brasero_async_task_manager_queue (BRASERO_ASYNC_TASK_MANAGER (self),
436
BRASERO_ASYNC_NORMAL,
446
brasero_io_job_free (BraseroIO *self,
450
/* NOTE: the callback_data member is never destroyed here since it would
451
* be destroyed in a thread different from the main loop.
452
* Either it's destroyed in the thread that called brasero_io_cancel ()
453
* or after all results are returned (and therefore in main loop).
454
* As a special case, some jobs like read directory contents have to
455
* return a dummy result to destroy the callback_data if the directory
457
* If the job happens to be the last to carry a reference then destroy
458
* it but do it in the main loop by sending a dummy result. */
460
/* NOTE2: that's also used for directory loading when there aren't any
461
* result to notify the caller that the operation has finished but that
462
* there weren't any result to report. */
463
if (job->callback_data) {
464
/* see atomically if we are the last to hold a lock:
465
* If so, and if cancelled is TRUE destroy it now since we are
466
* always cancelled (and destroyed in the main loop). Otherwise
467
* add a dummy result to destroy callback_data. */
468
if (g_atomic_int_dec_and_test (&job->callback_data->ref)) {
470
if (job->base->destroy)
471
job->base->destroy (job->base->object,
473
job->callback_data->callback_data);
475
g_free (job->callback_data);
478
brasero_io_return_result (self,
492
brasero_io_job_destroy (BraseroAsyncTaskManager *manager,
494
gpointer callback_data)
496
BraseroIOJob *job = callback_data;
498
/* If a job is destroyed we don't call the destroy callback since it
499
* otherwise it would be called in a different thread. All object that
500
* cancel io ops are doing it either in destroy () and therefore handle
501
* all destruction for callback_data or if they don't they usually don't
502
* pass any callback data anyway. */
503
/* NOTE: usually threads are cancelled from the main thread/loop and
504
* block until the active task is removed which means that if we called
505
* the destroy () then the destruction would be done in the main loop */
506
brasero_io_job_free (BRASERO_IO (manager), cancelled, job);
510
* That's when we need to mount a remote volume
513
struct _BraseroIOMount {
518
typedef struct _BraseroIOMount BraseroIOMount;
521
brasero_io_mount_enclosing_volume_cb (GObject *source,
522
GAsyncResult *result,
523
gpointer callback_data)
525
BraseroIOMount *mount = callback_data;
527
BRASERO_BURN_LOG ("Volume mounting operation result");
528
mount->result = g_file_mount_enclosing_volume_finish (G_FILE (source),
531
mount->finished = TRUE;
535
brasero_io_mount_enclosing_volume (BraseroIO *self,
537
GCancellable *cancel,
541
GMountOperation *operation;
542
BraseroIOMount mount = { NULL, };
544
operation = gtk_mount_operation_new (GTK_WINDOW (brasero_app_get_default ()));
545
g_file_mount_enclosing_volume (file,
549
brasero_io_mount_enclosing_volume_cb,
552
/* sleep and wait operation end */
553
while (!mount.finished && !g_cancellable_is_cancelled (cancel))
556
mounted = g_file_find_enclosing_mount (file, cancel, NULL);
558
BraseroIOPrivate *priv;
560
priv = BRASERO_IO_PRIVATE (self);
562
/* Keep these for later to unmount them */
564
g_mutex_lock (priv->lock);
565
priv->mounted = g_slist_prepend (priv->mounted, mounted);
566
g_mutex_unlock (priv->lock);
569
g_object_unref (mounted);
574
&& !g_cancellable_is_cancelled (cancel))
575
g_propagate_error (error, mount.error);
576
else if (mount.error)
577
g_error_free (mount.error);
579
BRASERO_BURN_LOG ("Parent volume is %s",
580
(mounted != NULL && !g_cancellable_is_cancelled (cancel))?
581
"mounted":"not mounted");
583
return (mounted != NULL && !g_cancellable_is_cancelled (cancel));
587
* This part deals with symlinks, that allows to get unique filenames by
588
* replacing any parent symlink by its target and check for recursive
593
brasero_io_check_for_parent_symlink (const gchar *escaped_uri,
594
GCancellable *cancel)
600
/* don't check if the node itself is a symlink since that'll be done */
601
file = g_file_new_for_uri (escaped_uri);
602
uri = g_file_get_uri (file);
603
parent = g_file_get_parent (file);
604
g_object_unref (file);
610
info = g_file_query_info (parent,
611
G_FILE_ATTRIBUTE_STANDARD_TYPE ","
612
G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
613
G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
614
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, /* don't follow symlinks */
620
/* NOTE: no need to check for broken symlinks since
621
* we wouldn't have reached this point otherwise */
622
if (g_file_info_get_is_symlink (info)) {
623
const gchar *target_path;
628
parent_uri = g_file_get_uri (parent);
629
target_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
631
/* check if this is not a relative path */
632
if (!g_path_is_absolute (target_path)) {
635
tmp = g_path_get_dirname (parent_uri);
636
new_root = g_build_path (G_DIR_SEPARATOR_S,
643
new_root = g_filename_to_uri (target_path, NULL, NULL);
645
newuri = g_build_path (G_DIR_SEPARATOR_S,
647
uri + strlen (parent_uri),
652
g_object_unref (parent);
655
parent = g_file_new_for_uri (new_root);
660
parent = g_file_get_parent (parent);
661
g_object_unref (tmp);
663
g_object_unref (info);
667
g_object_unref (parent);
673
brasero_io_get_uri_from_path (GFile *file,
678
if (!g_path_is_absolute (path))
679
file = g_file_resolve_relative_path (file, path);
681
file = g_file_new_for_path (path);
686
uri = g_file_get_uri (file);
687
g_object_unref (file);
692
brasero_io_check_symlink_target (GFile *parent,
700
target = g_file_info_get_symlink_target (info);
704
target_uri = brasero_io_get_uri_from_path (parent, target);
708
/* we check for circular dependency here :
709
* if the target is one of the parent of symlink */
710
size = strlen (target_uri);
711
uri = g_file_get_uri (parent);
713
if (!strncmp (target_uri, uri, size)
714
&& (*(uri + size) == '/' || *(uri + size) == '\0')) {
721
g_file_info_set_symlink_target (info, target_uri);
728
* Used to retrieve metadata for audio files
731
struct _BraserIOMetadataTask {
735
typedef struct _BraseroIOMetadataTask BraseroIOMetadataTask;
737
struct _BraseroIOMetadataCached {
738
guint64 last_modified;
739
BraseroMetadataInfo *info;
741
typedef struct _BraseroIOMetadataCached BraseroIOMetadataCached;
744
brasero_io_metadata_lookup_buffer (gconstpointer a, gconstpointer b)
746
const BraseroIOMetadataCached *cached = a;
747
const gchar *uri = b;
749
return strcmp (uri, cached->info->uri);
753
brasero_io_metadata_cached_free (BraseroIOMetadataCached *cached)
755
brasero_metadata_info_free (cached->info);
760
brasero_io_set_metadata_attributes (GFileInfo *info,
761
BraseroMetadataInfo *metadata)
763
g_file_info_set_attribute_int32 (info, BRASERO_IO_ISRC, metadata->isrc);
764
g_file_info_set_attribute_uint64 (info, BRASERO_IO_LEN, metadata->len);
766
if (metadata->artist)
767
g_file_info_set_attribute_string (info, BRASERO_IO_ARTIST, metadata->artist);
770
g_file_info_set_attribute_string (info, BRASERO_IO_TITLE, metadata->title);
773
g_file_info_set_attribute_string (info, BRASERO_IO_ALBUM, metadata->album);
776
g_file_info_set_attribute_string (info, BRASERO_IO_GENRE, metadata->genre);
778
if (metadata->composer)
779
g_file_info_set_attribute_string (info, BRASERO_IO_COMPOSER, metadata->composer);
781
g_file_info_set_attribute_boolean (info, BRASERO_IO_HAS_AUDIO, metadata->has_audio);
782
g_file_info_set_attribute_boolean (info, BRASERO_IO_HAS_VIDEO, metadata->has_video);
783
g_file_info_set_attribute_boolean (info, BRASERO_IO_IS_SEEKABLE, metadata->is_seekable);
785
if (metadata->snapshot)
786
g_file_info_set_attribute_object (info, BRASERO_IO_THUMBNAIL, G_OBJECT (metadata->snapshot));
788
/* FIXME: what about silences */
791
static BraseroMetadata *
792
brasero_io_find_metadata (BraseroIO *self,
793
GCancellable *cancel,
795
BraseroMetadataFlag flags,
799
BraseroIOPrivate *priv;
800
BraseroMetadata *metadata;
802
priv = BRASERO_IO_PRIVATE (self);
804
BRASERO_BURN_LOG ("Retrieving available metadata %s", uri);
806
/* First see if a metadata is running with the same uri and the same
807
* flags as us. In this case, acquire the lock and wait for the lock
808
* to become available which will mean that it has finished
809
* NOTE: since we will hold the lock another thread waiting to get
810
* an available metadata won't be able to have this one. */
811
for (iter = priv->metadata_running; iter; iter = iter->next) {
812
const gchar *metadata_uri;
813
BraseroMetadataFlag metadata_flags;
815
metadata = iter->data;
816
metadata_uri = brasero_metadata_get_uri (metadata);
818
/* It could a metadata that was running but failed to
819
* retrieve anything and is waiting to be inserted back
820
* in the available list. Ignore it. */
824
metadata_flags = brasero_metadata_get_flags (metadata);
826
if (((flags & metadata_flags) == flags)
827
&& !strcmp (uri, metadata_uri)) {
828
/* Found one: release the IO lock to let other threads
829
* do what they need to do then lock the metadata lock
830
* Let the thread that got the lock first move it back
831
* to waiting queue */
832
BRASERO_BURN_LOG ("Already ongoing search for %s", uri);
833
brasero_metadata_increase_listener_number (metadata);
838
/* Grab an available metadata (NOTE: there should always be at least one
839
* since we run 2 threads at max and have two metadatas available) */
840
while (!priv->metadatas) {
841
if (g_cancellable_is_cancelled (cancel))
844
g_mutex_unlock (priv->lock_metadata);
846
g_mutex_lock (priv->lock_metadata);
849
/* One metadata is finally available */
850
metadata = priv->metadatas->data;
852
/* Try to set it up for running */
853
if (!brasero_metadata_set_uri (metadata, flags, uri, error))
856
/* The metadata is ready for running put it in right queue */
857
brasero_metadata_increase_listener_number (metadata);
858
priv->metadatas = g_slist_remove (priv->metadatas, metadata);
859
priv->metadata_running = g_slist_prepend (priv->metadata_running, metadata);
865
brasero_io_wait_for_metadata (BraseroIO *self,
866
GCancellable *cancel,
868
BraseroMetadata *metadata,
869
BraseroMetadataInfo *meta_info)
873
BraseroIOPrivate *priv;
875
priv = BRASERO_IO_PRIVATE (self);
877
brasero_metadata_wait (metadata, cancel);
878
g_mutex_lock (priv->lock_metadata);
880
is_last = brasero_metadata_decrease_listener_number (metadata);
882
if (!g_cancellable_is_cancelled (cancel))
883
result = brasero_metadata_get_result (metadata, meta_info, NULL);
887
/* Only the last thread waiting for the result will put metadata back
888
* into the available queue and cache the results. */
890
g_mutex_unlock (priv->lock_metadata);
895
/* see if we should add it to the buffer */
896
if (meta_info->has_audio || meta_info->has_video) {
897
BraseroIOMetadataCached *cached;
899
cached = g_new0 (BraseroIOMetadataCached, 1);
900
cached->last_modified = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
902
cached->info = g_new0 (BraseroMetadataInfo, 1);
903
brasero_metadata_get_result (metadata, cached->info, NULL);
905
g_queue_push_head (priv->meta_buffer, cached);
906
if (g_queue_get_length (priv->meta_buffer) > MAX_BUFFERED_META) {
907
cached = g_queue_pop_tail (priv->meta_buffer);
908
brasero_io_metadata_cached_free (cached);
913
/* Make sure it is stopped */
914
BRASERO_BURN_LOG ("Stopping metadata information retrieval (%p)", metadata);
915
brasero_metadata_cancel (metadata);
917
priv->metadata_running = g_slist_remove (priv->metadata_running, metadata);
918
priv->metadatas = g_slist_append (priv->metadatas, metadata);
920
g_mutex_unlock (priv->lock_metadata);
926
brasero_io_get_metadata_info (BraseroIO *self,
927
GCancellable *cancel,
930
BraseroMetadataFlag flags,
931
BraseroMetadataInfo *meta_info)
933
BraseroMetadata *metadata = NULL;
934
BraseroIOPrivate *priv;
938
if (g_cancellable_is_cancelled (cancel))
941
priv = BRASERO_IO_PRIVATE (self);
943
mime = g_file_info_get_content_type (info);
945
&& (!strncmp (mime, "image/", 6)
946
|| !strcmp (mime, "text/plain")
947
|| !strcmp (mime, "application/x-cue") /* this one make gstreamer crash */
948
|| !strcmp (mime, "application/x-cd-image")
949
|| !strcmp (mime, "application/octet-stream")))
952
BRASERO_BURN_LOG ("Retrieving metadata info");
953
g_mutex_lock (priv->lock_metadata);
955
/* Seek in the buffer if we have already explored these metadata. Check
956
* the info last modified time in case a result should be updated. */
957
node = g_queue_find_custom (priv->meta_buffer,
959
brasero_io_metadata_lookup_buffer);
961
guint64 last_modified;
962
BraseroIOMetadataCached *cached;
965
last_modified = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
966
if (last_modified == cached->last_modified) {
967
if (flags & BRASERO_METADATA_FLAG_THUMBNAIL) {
968
/* If there isn't any snapshot retry */
969
if (cached->info->snapshot) {
970
brasero_metadata_info_copy (meta_info, cached->info);
971
g_mutex_unlock (priv->lock_metadata);
976
brasero_metadata_info_copy (meta_info, cached->info);
977
g_mutex_unlock (priv->lock_metadata);
982
/* Found the same URI but it didn't have all required flags so
983
* we'll get another metadata information; Remove it from the
984
* queue => no same URI twice */
985
g_queue_remove (priv->meta_buffer, cached);
986
brasero_io_metadata_cached_free (cached);
988
BRASERO_BURN_LOG ("Updating cache information for %s", uri);
991
/* Find a metadata */
992
metadata = brasero_io_find_metadata (self, cancel, uri, flags, NULL);
993
g_mutex_unlock (priv->lock_metadata);
998
return brasero_io_wait_for_metadata (self,
1006
* Used to get information about files
1010
brasero_io_get_file_info_thread_real (BraseroAsyncTaskManager *manager,
1011
GCancellable *cancel,
1013
BraseroIOFlags options,
1016
gchar attributes [256] = {G_FILE_ATTRIBUTE_STANDARD_NAME ","
1017
G_FILE_ATTRIBUTE_STANDARD_SIZE ","
1018
G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
1019
G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
1020
G_FILE_ATTRIBUTE_STANDARD_TYPE};
1021
GError *local_error = NULL;
1022
gboolean should_thumbnail;
1025
if (g_cancellable_is_cancelled (cancel))
1028
if (options & BRASERO_IO_INFO_PERM)
1029
strcat (attributes, "," G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
1030
if (options & BRASERO_IO_INFO_MIME)
1031
strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
1032
if (options & BRASERO_IO_INFO_ICON)
1033
strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_ICON);
1034
if (options & BRASERO_IO_INFO_METADATA_THUMBNAIL)
1035
strcat (attributes, "," G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
1037
/* if retrieving metadata we need this one to check if a possible result
1038
* in cache should be updated or used */
1039
if (options & BRASERO_IO_INFO_METADATA)
1040
strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_SIZE);
1042
info = g_file_query_info (file,
1044
(options & BRASERO_IO_INFO_FOLLOW_SYMLINK)?G_FILE_QUERY_INFO_NONE:G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, /* follow symlinks by default*/
1048
if (local_error && local_error->code == G_IO_ERROR_NOT_MOUNTED) {
1051
BRASERO_BURN_LOG ("Starting to mount parent volume");
1052
g_error_free (local_error);
1055
/* try to mount whatever has to be mounted */
1056
/* NOTE: of course, we block a thread but one advantage
1057
* is that we won't have many queries to mount the same
1058
* remote volume at the same time. */
1059
res = brasero_io_mount_enclosing_volume (BRASERO_IO (manager),
1066
return brasero_io_get_file_info_thread_real (manager,
1073
g_propagate_error (error, local_error);
1077
if (g_file_info_get_is_symlink (info)) {
1080
parent = g_file_get_parent (file);
1081
if (!brasero_io_check_symlink_target (parent, info)) {
1084
BRASERO_ERROR_SYMLINK_LOOP,
1085
_("Recursive symbolic link"));
1087
g_object_unref (info);
1088
g_object_unref (file);
1089
g_object_unref (parent);
1092
g_object_unref (parent);
1095
should_thumbnail = FALSE;
1096
if (options & BRASERO_IO_INFO_METADATA_THUMBNAIL) {
1099
path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
1103
pixbuf = gdk_pixbuf_new_from_file (path, NULL);
1105
g_file_info_set_attribute_object (info,
1106
BRASERO_IO_THUMBNAIL,
1108
g_object_unref (pixbuf);
1111
should_thumbnail = TRUE;
1113
else if (!g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED))
1114
should_thumbnail = TRUE;
1117
/* see if we are supposed to get metadata for this file (provided it's
1118
* an audio file of course). */
1119
if ((g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR
1120
|| g_file_info_get_file_type (info) == G_FILE_TYPE_SYMBOLIC_LINK)
1121
&& (options & BRASERO_IO_INFO_METADATA)) {
1122
BraseroMetadataInfo metadata = { NULL };
1123
BraseroMetadataFlag flags;
1127
flags = ((options & BRASERO_IO_INFO_METADATA_MISSING_CODEC) ? BRASERO_METADATA_FLAG_MISSING : 0)|
1128
((should_thumbnail) ? BRASERO_METADATA_FLAG_THUMBNAIL : 0);
1130
uri = g_file_get_uri (file);
1131
result = brasero_io_get_metadata_info (BRASERO_IO (manager),
1140
brasero_io_set_metadata_attributes (info, &metadata);
1142
brasero_metadata_info_clear (&metadata);
1148
static BraseroAsyncTaskResult
1149
brasero_io_get_file_info_thread (BraseroAsyncTaskManager *manager,
1150
GCancellable *cancel,
1151
gpointer callback_data)
1153
BraseroIOJob *job = callback_data;
1154
gchar *file_uri = NULL;
1155
GError *error = NULL;
1159
if (job->options & BRASERO_IO_INFO_CHECK_PARENT_SYMLINK) {
1160
/* If we want to make sure a directory is not added twice we have to make sure
1161
* that it doesn't have a symlink as parent otherwise "/home/Foo/Bar" with Foo
1162
* as a symlink pointing to /tmp would be seen as a different file from /tmp/Bar
1163
* It would be much better if we could use the inode numbers provided by gnome_vfs
1164
* unfortunately they are guint64 and can't be used in hash tables as keys.
1165
* Therefore we check parents up to root to see if there are symlinks and if so
1166
* we get a path without symlinks in it. This is done only for local file */
1167
file_uri = brasero_io_check_for_parent_symlink (job->uri, cancel);
1170
if (g_cancellable_is_cancelled (cancel)) {
1172
return BRASERO_ASYNC_TASK_FINISHED;
1175
file = g_file_new_for_uri (file_uri?file_uri:job->uri);
1176
info = brasero_io_get_file_info_thread_real (manager,
1182
/* do this to have a very nice URI:
1183
* for example: file://pouet instead of file://../directory/pouet */
1185
file_uri = g_file_get_uri (file);
1186
g_object_unref (file);
1188
brasero_io_return_result (BRASERO_IO (manager),
1193
job->callback_data);
1196
return BRASERO_ASYNC_TASK_FINISHED;
1199
static const BraseroAsyncTaskType info_type = {
1200
brasero_io_get_file_info_thread,
1201
brasero_io_job_destroy
1205
brasero_io_new_file_info_job (BraseroIO *self,
1207
const BraseroIOJobBase *base,
1208
BraseroIOFlags options,
1209
BraseroIOResultCallbackData *callback_data)
1213
job = g_new0 (BraseroIOJob, 1);
1214
brasero_io_set_job (job,
1220
brasero_io_push_job (self, job, &info_type);
1224
brasero_io_get_file_info (BraseroIO *self,
1226
const BraseroIOJobBase *base,
1227
BraseroIOFlags options,
1230
BraseroIOResultCallbackData *callback_data = NULL;
1233
callback_data = g_new0 (BraseroIOResultCallbackData, 1);
1234
callback_data->callback_data = user_data;
1237
brasero_io_new_file_info_job (self, uri, base, options, callback_data);
1241
* Used to parse playlists
1244
#ifdef BUILD_PLAYLIST
1246
struct _BraseroIOPlaylist {
1250
typedef struct _BraseroIOPlaylist BraseroIOPlaylist;
1253
brasero_io_playlist_clear (BraseroIOPlaylist *data)
1255
g_slist_foreach (data->uris, (GFunc) g_free, NULL);
1256
g_slist_free (data->uris);
1258
g_free (data->title);
1262
brasero_io_add_playlist_entry_parsed_cb (TotemPlParser *parser,
1264
GHashTable *metadata,
1265
BraseroIOPlaylist *data)
1267
data->uris = g_slist_prepend (data->uris, g_strdup (uri));
1271
brasero_io_start_playlist_cb (TotemPlParser *parser,
1273
GHashTable *metadata,
1274
BraseroIOPlaylist *data)
1278
title = g_hash_table_lookup (metadata, TOTEM_PL_PARSER_FIELD_TITLE);
1283
data->title = g_strdup (title);
1287
brasero_io_parse_playlist_get_uris (const gchar *uri,
1288
BraseroIOPlaylist *playlist,
1292
TotemPlParser *parser;
1294
parser = totem_pl_parser_new ();
1295
g_signal_connect (parser,
1297
G_CALLBACK (brasero_io_start_playlist_cb),
1299
g_signal_connect (parser,
1301
G_CALLBACK (brasero_io_add_playlist_entry_parsed_cb),
1304
if (g_object_class_find_property (G_OBJECT_GET_CLASS (parser), "recurse"))
1305
g_object_set (G_OBJECT (parser), "recurse", FALSE, NULL);
1307
result = totem_pl_parser_parse (parser, uri, TRUE);
1308
g_object_unref (parser);
1313
BRASERO_ERROR_GENERAL,
1314
_("The file does not appear to be a playlist"));
1322
static BraseroAsyncTaskResult
1323
brasero_io_parse_playlist_thread (BraseroAsyncTaskManager *manager,
1324
GCancellable *cancel,
1325
gpointer callback_data)
1330
GError *error = NULL;
1331
BraseroIOJob *job = callback_data;
1332
BraseroIOPlaylist data = { NULL, };
1334
result = brasero_io_parse_playlist_get_uris (job->uri, &data, &error);
1336
brasero_io_return_result (BRASERO_IO (manager),
1341
job->callback_data);
1342
return BRASERO_ASYNC_TASK_FINISHED;
1345
if (g_cancellable_is_cancelled (cancel))
1346
return BRASERO_ASYNC_TASK_FINISHED;
1348
/* that's finished; Send the title */
1349
info = g_file_info_new ();
1350
g_file_info_set_attribute_boolean (info,
1351
BRASERO_IO_IS_PLAYLIST,
1353
g_file_info_set_attribute_uint32 (info,
1354
BRASERO_IO_PLAYLIST_ENTRIES_NUM,
1355
g_slist_length (data.uris));
1357
g_file_info_set_attribute_string (info,
1358
BRASERO_IO_PLAYLIST_TITLE,
1361
brasero_io_return_result (BRASERO_IO (manager),
1366
job->callback_data);
1368
/* Now get information about each file in the list.
1369
* Reverse order of list to get a correct order for entries. */
1370
data.uris = g_slist_reverse (data.uris);
1371
for (iter = data.uris; iter; iter = iter->next) {
1374
GFileInfo *child_info;
1377
if (g_cancellable_is_cancelled (cancel))
1380
file = g_file_new_for_uri (child);
1381
child_info = brasero_io_get_file_info_thread_real (manager,
1386
g_object_unref (file);
1391
brasero_io_return_result (BRASERO_IO (manager),
1396
job->callback_data);
1399
brasero_io_playlist_clear (&data);
1400
return BRASERO_ASYNC_TASK_FINISHED;
1403
static const BraseroAsyncTaskType playlist_type = {
1404
brasero_io_parse_playlist_thread,
1405
brasero_io_job_destroy
1409
brasero_io_parse_playlist (BraseroIO *self,
1411
const BraseroIOJobBase *base,
1412
BraseroIOFlags options,
1416
BraseroIOResultCallbackData *callback_data = NULL;
1419
callback_data = g_new0 (BraseroIOResultCallbackData, 1);
1420
callback_data->callback_data = user_data;
1423
job = g_new0 (BraseroIOJob, 1);
1424
brasero_io_set_job (job,
1430
brasero_io_push_job (self, job, &playlist_type);
1436
* Used to count the number of files under a directory and the children size
1439
struct _BraseroIOCountData {
1446
guint files_invalid;
1449
gboolean progress_started;
1451
typedef struct _BraseroIOCountData BraseroIOCountData;
1454
brasero_io_get_file_count_destroy (BraseroAsyncTaskManager *manager,
1456
gpointer callback_data)
1458
BraseroIOCountData *data = callback_data;
1460
g_slist_foreach (data->uris, (GFunc) g_free, NULL);
1461
g_slist_free (data->uris);
1463
g_slist_foreach (data->children, (GFunc) g_object_unref, NULL);
1464
g_slist_free (data->children);
1466
brasero_io_job_progress_report_stop (BRASERO_IO (manager), callback_data);
1468
brasero_io_job_free (BRASERO_IO (manager), cancelled, callback_data);
1471
#ifdef BUILD_PLAYLIST
1474
brasero_io_get_file_count_process_playlist (BraseroIO *self,
1475
GCancellable *cancel,
1476
BraseroIOCountData *data,
1479
BraseroIOPlaylist playlist = {NULL, };
1482
if (!brasero_io_parse_playlist_get_uris (uri, &playlist, NULL))
1485
for (iter = playlist.uris; iter; iter = iter->next) {
1489
BraseroMetadataInfo metadata = { NULL, };
1491
child_uri = iter->data;
1494
info = g_file_info_new ();
1495
result = brasero_io_get_metadata_info (self,
1499
((data->job.options & BRASERO_IO_INFO_METADATA_MISSING_CODEC) ? BRASERO_METADATA_FLAG_MISSING : 0) |
1500
((data->job.options & BRASERO_IO_INFO_METADATA_THUMBNAIL) ? BRASERO_METADATA_FLAG_THUMBNAIL : 0),
1504
data->total_b += metadata.len;
1506
data->files_invalid ++;
1508
brasero_metadata_info_clear (&metadata);
1509
g_object_unref (info);
1512
brasero_io_playlist_clear (&playlist);
1519
brasero_io_get_file_count_process_file (BraseroIO *self,
1520
GCancellable *cancel,
1521
BraseroIOCountData *data,
1525
if (data->job.options & BRASERO_IO_INFO_METADATA) {
1526
BraseroMetadataInfo metadata = { NULL, };
1527
gboolean result = FALSE;
1530
child_uri = g_file_get_uri (file);
1531
result = brasero_io_get_metadata_info (self,
1535
((data->job.options & BRASERO_IO_INFO_METADATA_MISSING_CODEC) ? BRASERO_METADATA_FLAG_MISSING : 0) |
1536
((data->job.options & BRASERO_IO_INFO_METADATA_THUMBNAIL) ? BRASERO_METADATA_FLAG_THUMBNAIL : 0),
1539
data->total_b += metadata.len;
1541
#ifdef BUILD_PLAYLIST
1543
/* see if that's a playlist (and if we have recursive on). */
1544
else if (data->job.options & BRASERO_IO_INFO_RECURSIVE) {
1547
mime = g_file_info_get_content_type (info);
1549
&& (!strcmp (mime, "audio/x-scpls")
1550
|| !strcmp (mime, "audio/x-ms-asx")
1551
|| !strcmp (mime, "audio/x-mp3-playlist")
1552
|| !strcmp (mime, "audio/x-mpegurl"))) {
1553
if (!brasero_io_get_file_count_process_playlist (self, cancel, data, child_uri))
1554
data->files_invalid ++;
1557
data->files_invalid ++;
1563
data->files_invalid ++;
1565
brasero_metadata_info_clear (&metadata);
1570
data->total_b += g_file_info_get_size (info);
1574
brasero_io_get_file_count_process_directory (BraseroIO *self,
1575
GCancellable *cancel,
1576
BraseroIOCountData *data)
1580
GError *error = NULL;
1581
GFileEnumerator *enumerator;
1582
gchar attributes [512] = {G_FILE_ATTRIBUTE_STANDARD_NAME ","
1583
G_FILE_ATTRIBUTE_STANDARD_SIZE ","
1584
G_FILE_ATTRIBUTE_STANDARD_TYPE };
1586
if ((data->job.options & BRASERO_IO_INFO_METADATA)
1587
&& (data->job.options & BRASERO_IO_INFO_RECURSIVE))
1588
strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
1590
file = data->children->data;
1591
data->children = g_slist_remove (data->children, file);
1593
enumerator = g_file_enumerate_children (file,
1595
(data->job.options & BRASERO_IO_INFO_FOLLOW_SYMLINK)?G_FILE_QUERY_INFO_NONE:G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, /* follow symlinks by default*/
1599
g_object_unref (file);
1603
while ((info = g_file_enumerator_next_file (enumerator, cancel, &error)) || error) {
1606
if (g_cancellable_is_cancelled (cancel)) {
1607
g_object_unref (info);
1614
g_error_free (error);
1617
data->files_invalid ++;
1621
child = g_file_get_child (file, g_file_info_get_name (info));
1623
if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR
1624
|| g_file_info_get_file_type (info) == G_FILE_TYPE_SYMBOLIC_LINK) {
1625
brasero_io_get_file_count_process_file (self, cancel, data, child, info);
1626
g_object_unref (child);
1628
else if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
1629
data->children = g_slist_prepend (data->children, child);
1631
g_object_unref (child);
1633
g_object_unref (info);
1636
g_file_enumerator_close (enumerator, cancel, NULL);
1637
g_object_unref (enumerator);
1638
g_object_unref (file);
1642
brasero_io_get_file_count_start (BraseroIO *self,
1643
GCancellable *cancel,
1644
BraseroIOCountData *data,
1649
gchar attributes [512] = {G_FILE_ATTRIBUTE_STANDARD_NAME ","
1650
G_FILE_ATTRIBUTE_STANDARD_SIZE ","
1651
G_FILE_ATTRIBUTE_STANDARD_TYPE };
1653
if ((data->job.options & BRASERO_IO_INFO_METADATA)
1654
&& (data->job.options & BRASERO_IO_INFO_RECURSIVE))
1655
strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
1657
file = g_file_new_for_uri (uri);
1658
info = g_file_query_info (file,
1660
(data->job.options & BRASERO_IO_INFO_FOLLOW_SYMLINK)?G_FILE_QUERY_INFO_NONE:G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, /* follow symlinks by default*/
1666
g_object_unref (file);
1667
data->files_invalid ++;
1671
if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR
1672
|| g_file_info_get_file_type (info) == G_FILE_TYPE_SYMBOLIC_LINK) {
1673
brasero_io_get_file_count_process_file (self, cancel, data, file, info);
1674
g_object_unref (file);
1676
else if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
1677
if (data->job.options & BRASERO_IO_INFO_RECURSIVE)
1678
data->children = g_slist_prepend (data->children, file);
1680
g_object_unref (file);
1683
g_object_unref (file);
1685
g_object_unref (info);
1690
brasero_io_get_file_count_progress_cb (BraseroIOJob *job,
1691
BraseroIOJobProgress *progress)
1693
BraseroIOCountData *data = (BraseroIOCountData *) job;
1695
progress->read_b = data->total_b;
1696
progress->total_b = data->total_b;
1697
progress->files_num = data->files_num;
1698
progress->files_invalid = data->files_invalid;
1701
static BraseroAsyncTaskResult
1702
brasero_io_get_file_count_thread (BraseroAsyncTaskManager *manager,
1703
GCancellable *cancel,
1704
gpointer callback_data)
1706
BraseroIOCountData *data = callback_data;
1710
if (data->children) {
1711
brasero_io_get_file_count_process_directory (BRASERO_IO (manager), cancel, data);
1712
return BRASERO_ASYNC_TASK_RESCHEDULE;
1714
else if (!data->uris) {
1715
info = g_file_info_new ();
1717
/* set GFileInfo information */
1718
g_file_info_set_attribute_uint32 (info, BRASERO_IO_COUNT_INVALID, data->files_invalid);
1719
g_file_info_set_attribute_uint64 (info, BRASERO_IO_COUNT_SIZE, data->total_b);
1720
g_file_info_set_attribute_uint32 (info, BRASERO_IO_COUNT_NUM, data->files_num);
1722
brasero_io_return_result (BRASERO_IO (manager),
1727
data->job.callback_data);
1729
return BRASERO_ASYNC_TASK_FINISHED;
1732
if (!data->progress_started) {
1733
brasero_io_job_progress_report_start (BRASERO_IO (manager),
1735
brasero_io_get_file_count_progress_cb);
1736
data->progress_started = 1;
1739
uri = data->uris->data;
1740
data->uris = g_slist_remove (data->uris, uri);
1742
brasero_io_get_file_count_start (BRASERO_IO (manager), cancel, data, uri);
1745
return BRASERO_ASYNC_TASK_RESCHEDULE;
1748
static const BraseroAsyncTaskType count_type = {
1749
brasero_io_get_file_count_thread,
1750
brasero_io_get_file_count_destroy
1754
brasero_io_get_file_count (BraseroIO *self,
1756
const BraseroIOJobBase *base,
1757
BraseroIOFlags options,
1760
BraseroIOCountData *data;
1761
BraseroIOResultCallbackData *callback_data = NULL;
1764
callback_data = g_new0 (BraseroIOResultCallbackData, 1);
1765
callback_data->callback_data = user_data;
1768
data = g_new0 (BraseroIOCountData, 1);
1770
for (; uris; uris = uris->next)
1771
data->uris = g_slist_prepend (data->uris, g_strdup (uris->data));
1773
brasero_io_set_job (BRASERO_IO_JOB (data),
1779
brasero_io_push_job (self, BRASERO_IO_JOB (data), &count_type);
1783
* Used to explore directories
1786
struct _BraseroIOContentsData {
1790
typedef struct _BraseroIOContentsData BraseroIOContentsData;
1793
brasero_io_load_directory_destroy (BraseroAsyncTaskManager *manager,
1795
gpointer callback_data)
1797
BraseroIOContentsData *data = callback_data;
1799
g_slist_foreach (data->children, (GFunc) g_object_unref, NULL);
1800
g_slist_free (data->children);
1802
brasero_io_job_free (BRASERO_IO (manager), cancelled, BRASERO_IO_JOB (data));
1805
#ifdef BUILD_PLAYLIST
1808
brasero_io_load_directory_playlist (BraseroIO *self,
1809
GCancellable *cancel,
1810
BraseroIOContentsData *data,
1812
const gchar *attributes)
1814
BraseroIOPlaylist playlist = {NULL, };
1817
if (!brasero_io_parse_playlist_get_uris (uri, &playlist, NULL))
1820
for (iter = playlist.uris; iter; iter = iter->next) {
1825
BraseroMetadataInfo metadata = { NULL, };
1827
child_uri = iter->data;
1829
file = g_file_new_for_uri (child_uri);
1830
info = g_file_query_info (file,
1832
G_FILE_QUERY_INFO_NONE, /* follow symlinks */
1836
g_object_unref (file);
1840
result = brasero_io_get_metadata_info (self,
1844
((data->job.options & BRASERO_IO_INFO_METADATA_MISSING_CODEC) ? BRASERO_METADATA_FLAG_MISSING : 0) |
1845
((data->job.options & BRASERO_IO_INFO_METADATA_THUMBNAIL) ? BRASERO_METADATA_FLAG_THUMBNAIL : 0),
1849
brasero_io_set_metadata_attributes (info, &metadata);
1850
brasero_io_return_result (self,
1855
data->job.callback_data);
1858
g_object_unref (info);
1860
brasero_metadata_info_clear (&metadata);
1862
g_object_unref (file);
1865
brasero_io_playlist_clear (&playlist);
1871
static BraseroAsyncTaskResult
1872
brasero_io_load_directory_thread (BraseroAsyncTaskManager *manager,
1873
GCancellable *cancel,
1874
gpointer callback_data)
1876
gchar attributes [512] = {G_FILE_ATTRIBUTE_STANDARD_NAME ","
1877
G_FILE_ATTRIBUTE_STANDARD_SIZE ","
1878
G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
1879
G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
1880
G_FILE_ATTRIBUTE_STANDARD_TYPE };
1881
BraseroIOContentsData *data = callback_data;
1882
GFileEnumerator *enumerator;
1883
GError *error = NULL;
1887
if (data->job.options & BRASERO_IO_INFO_PERM)
1888
strcat (attributes, "," G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
1890
if (data->job.options & BRASERO_IO_INFO_MIME)
1891
strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
1892
else if ((data->job.options & BRASERO_IO_INFO_METADATA)
1893
&& (data->job.options & BRASERO_IO_INFO_RECURSIVE))
1894
strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
1896
if (data->job.options & BRASERO_IO_INFO_ICON)
1897
strcat (attributes, "," G_FILE_ATTRIBUTE_STANDARD_ICON);
1899
if (data->children) {
1900
file = data->children->data;
1901
data->children = g_slist_remove (data->children, file);
1904
file = g_file_new_for_uri (data->job.uri);
1906
enumerator = g_file_enumerate_children (file,
1908
(data->job.options & BRASERO_IO_INFO_FOLLOW_SYMLINK)?G_FILE_QUERY_INFO_NONE:G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, /* follow symlinks by default*/
1913
gchar *directory_uri;
1915
directory_uri = g_file_get_uri (file);
1916
brasero_io_return_result (BRASERO_IO (manager),
1921
data->job.callback_data);
1922
g_free (directory_uri);
1923
g_object_unref (file);
1926
return BRASERO_ASYNC_TASK_RESCHEDULE;
1928
return BRASERO_ASYNC_TASK_FINISHED;
1931
while ((info = g_file_enumerator_next_file (enumerator, cancel, NULL))) {
1936
name = g_file_info_get_name (info);
1937
if (g_cancellable_is_cancelled (cancel)) {
1938
g_object_unref (info);
1943
&& (name [1] == '\0'
1944
|| (name [1] == '.' && name [2] == '\0'))) {
1945
g_object_unref (info);
1949
child = g_file_get_child (file, name);
1953
child_uri = g_file_get_uri (child);
1955
/* special case for symlinks */
1956
if (g_file_info_get_is_symlink (info)) {
1957
if (!brasero_io_check_symlink_target (file, info)) {
1958
error = g_error_new (BRASERO_ERROR,
1959
BRASERO_ERROR_SYMLINK_LOOP,
1960
_("Recursive symbolic link"));
1962
/* since we checked for the existence of the file
1963
* an error means a looping symbolic link */
1964
brasero_io_return_result (BRASERO_IO (manager),
1969
data->job.callback_data);
1972
g_object_unref (info);
1973
g_object_unref (child);
1978
if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
1979
brasero_io_return_result (BRASERO_IO (manager),
1984
data->job.callback_data);
1986
if (data->job.options & BRASERO_IO_INFO_RECURSIVE)
1987
data->children = g_slist_prepend (data->children, child);
1989
g_object_unref (child);
1995
if (data->job.options & BRASERO_IO_INFO_METADATA) {
1996
BraseroMetadataInfo metadata = {NULL, };
1999
/* add metadata information to this file */
2000
result = brasero_io_get_metadata_info (BRASERO_IO (manager),
2004
((data->job.options & BRASERO_IO_INFO_METADATA_MISSING_CODEC) ? BRASERO_METADATA_FLAG_MISSING : 0) |
2005
((data->job.options & BRASERO_IO_INFO_METADATA_THUMBNAIL) ? BRASERO_METADATA_FLAG_THUMBNAIL : 0),
2009
brasero_io_set_metadata_attributes (info, &metadata);
2011
#ifdef BUILD_PLAYLIST
2013
else if (data->job.options & BRASERO_IO_INFO_RECURSIVE) {
2016
mime = g_file_info_get_content_type (info);
2018
&& (!strcmp (mime, "audio/x-scpls")
2019
|| !strcmp (mime, "audio/x-ms-asx")
2020
|| !strcmp (mime, "audio/x-mp3-playlist")
2021
|| !strcmp (mime, "audio/x-mpegurl")))
2022
brasero_io_load_directory_playlist (BRASERO_IO (manager),
2031
brasero_metadata_info_clear (&metadata);
2034
brasero_io_return_result (BRASERO_IO (manager),
2039
data->job.callback_data);
2041
g_object_unref (child);
2044
g_file_enumerator_close (enumerator, NULL, NULL);
2045
g_object_unref (enumerator);
2046
g_object_unref (file);
2049
return BRASERO_ASYNC_TASK_RESCHEDULE;
2051
return BRASERO_ASYNC_TASK_FINISHED;
2054
static const BraseroAsyncTaskType contents_type = {
2055
brasero_io_load_directory_thread,
2056
brasero_io_load_directory_destroy
2060
brasero_io_load_directory (BraseroIO *self,
2062
const BraseroIOJobBase *base,
2063
BraseroIOFlags options,
2066
BraseroIOContentsData *data;
2067
BraseroIOResultCallbackData *callback_data = NULL;
2070
callback_data = g_new0 (BraseroIOResultCallbackData, 1);
2071
callback_data->callback_data = user_data;
2074
data = g_new0 (BraseroIOContentsData, 1);
2075
brasero_io_set_job (BRASERO_IO_JOB (data),
2081
brasero_io_push_job (self, BRASERO_IO_JOB (data), &contents_type);
2085
* to evaluate the contents of a medium or image async
2087
struct _BraseroIOImageContentsData {
2091
gint64 session_block;
2094
typedef struct _BraseroIOImageContentsData BraseroIOImageContentsData;
2097
brasero_io_image_directory_contents_destroy (BraseroAsyncTaskManager *manager,
2099
gpointer callback_data)
2101
BraseroIOImageContentsData *data = callback_data;
2103
g_free (data->dev_image);
2104
brasero_io_job_free (BRASERO_IO (manager), cancelled, BRASERO_IO_JOB (data));
2107
static BraseroAsyncTaskResult
2108
brasero_io_image_directory_contents_thread (BraseroAsyncTaskManager *manager,
2109
GCancellable *cancel,
2110
gpointer callback_data)
2112
BraseroIOImageContentsData *data = callback_data;
2113
BraseroDeviceHandle *handle;
2114
GList *children, *iter;
2115
GError *error = NULL;
2118
handle = brasero_device_handle_open (data->job.uri, FALSE, NULL);
2119
vol = brasero_volume_source_open_device_handle (handle, &error);
2121
brasero_device_handle_close (handle);
2122
brasero_io_return_result (BRASERO_IO (manager),
2127
data->job.callback_data);
2128
return BRASERO_ASYNC_TASK_FINISHED;
2131
children = brasero_volume_load_directory_contents (vol,
2132
data->session_block,
2135
brasero_volume_source_close (vol);
2136
brasero_device_handle_close (handle);
2138
for (iter = children; iter; iter = iter->next) {
2139
BraseroVolFile *file;
2144
info = g_file_info_new ();
2145
g_file_info_set_file_type (info, file->isdir? G_FILE_TYPE_DIRECTORY:G_FILE_TYPE_REGULAR);
2146
g_file_info_set_name (info, BRASERO_VOLUME_FILE_NAME (file));
2149
g_file_info_set_attribute_int64 (info,
2150
BRASERO_IO_DIR_CONTENTS_ADDR,
2151
file->specific.dir.address);
2153
g_file_info_set_size (info, BRASERO_VOLUME_FILE_SIZE (file));
2155
brasero_io_return_result (BRASERO_IO (manager),
2160
data->job.callback_data);
2163
g_list_foreach (children, (GFunc) brasero_volume_file_free, NULL);
2164
g_list_free (children);
2166
return BRASERO_ASYNC_TASK_FINISHED;
2169
static const BraseroAsyncTaskType image_contents_type = {
2170
brasero_io_image_directory_contents_thread,
2171
brasero_io_image_directory_contents_destroy
2175
brasero_io_load_image_directory (BraseroIO *self,
2176
const gchar *dev_image,
2177
gint64 session_block,
2179
const BraseroIOJobBase *base,
2180
BraseroIOFlags options,
2183
BraseroIOImageContentsData *data;
2184
BraseroIOResultCallbackData *callback_data = NULL;
2187
callback_data = g_new0 (BraseroIOResultCallbackData, 1);
2188
callback_data->callback_data = user_data;
2191
data = g_new0 (BraseroIOImageContentsData, 1);
2192
data->block = block;
2193
data->session_block = session_block;
2195
brasero_io_set_job (BRASERO_IO_JOB (data),
2201
brasero_io_push_job (self,
2202
BRASERO_IO_JOB (data),
2203
&image_contents_type);
2208
* That's for file transfer
2211
struct _BraseroIOXferPair {
2215
typedef struct _BraseroIOXferPair BraseroIOXferPair;
2217
struct _BraseroIOXferData {
2218
BraseroIOCountData count;
2219
BraseroIOJobProgress *progress;
2223
guint64 current_read_b;
2224
guint64 current_total_b;
2228
GMutex *current_lock;
2233
typedef struct _BraseroIOXferData BraseroIOXferData;
2236
brasero_io_xfer_pair_free (BraseroIOXferPair *pair)
2238
g_object_unref (pair->src);
2239
g_free (pair->dest);
2245
brasero_io_xfer_destroy (BraseroAsyncTaskManager *manager,
2247
gpointer callback_data)
2249
BraseroIOXferData *data = callback_data;
2251
g_slist_foreach (data->pairs, (GFunc) brasero_io_xfer_pair_free, NULL);
2252
g_slist_free (data->pairs);
2253
g_free (data->dest_path);
2255
g_mutex_free (data->current_lock);
2257
/* no need to stop progress report as the following function will do it */
2258
brasero_io_get_file_count_destroy (manager, cancelled, callback_data);
2262
brasero_io_xfer_progress_cb (goffset current_num_bytes,
2263
goffset total_num_bytes,
2264
gpointer callback_data)
2266
BraseroIOXferData *data = callback_data;
2268
data->current_read_b = current_num_bytes;
2269
data->current_total_b = total_num_bytes;
2272
static BraseroAsyncTaskResult
2273
brasero_io_xfer_file_thread (BraseroIOXferData *data,
2274
GCancellable *cancel,
2276
const gchar *dest_path,
2282
g_mutex_lock (data->current_lock);
2283
data->current = src;
2284
g_mutex_unlock (data->current_lock);
2286
dest = g_file_new_for_path (dest_path);
2287
result = g_file_copy (src,
2289
G_FILE_COPY_ALL_METADATA,
2291
brasero_io_xfer_progress_cb,
2294
g_object_unref (dest);
2296
data->read_b += data->current_total_b;
2297
data->current_read_b = 0;
2299
g_mutex_lock (data->current_lock);
2300
data->current = NULL;
2301
g_mutex_unlock (data->current_lock);
2307
brasero_io_xfer_recursive_thread (BraseroIOXferData *data,
2308
GCancellable *cancel,
2310
const gchar *dest_path,
2314
GFileEnumerator *enumerator;
2316
enumerator = g_file_enumerate_children (src,
2317
G_FILE_ATTRIBUTE_STANDARD_TYPE ","
2318
G_FILE_ATTRIBUTE_STANDARD_NAME,
2319
G_FILE_QUERY_INFO_NONE, /* follow symlinks by default */
2325
while ((info = g_file_enumerator_next_file (enumerator, cancel, NULL))) {
2330
if (g_cancellable_is_cancelled (cancel)) {
2338
src_child = g_file_get_child (src, g_file_info_get_name (info));
2339
dest_child = g_build_path (G_DIR_SEPARATOR_S,
2341
g_file_info_get_name (info),
2344
if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
2345
/* Create a directory with the same name and keep it for
2347
* Note: if that fails don't bother. */
2348
if (!g_mkdir (dest_child, 700)) {
2349
BraseroIOXferPair *new_pair;
2351
new_pair = g_new0 (BraseroIOXferPair, 1);
2352
new_pair->src = src_child;
2353
new_pair->dest = dest_child;
2354
data->pairs = g_slist_prepend (data->pairs, new_pair);
2358
result = brasero_io_xfer_file_thread (data,
2364
g_free (dest_child);
2365
g_object_unref (src_child);
2368
g_object_unref (info);
2371
g_file_enumerator_close (enumerator, cancel, NULL);
2372
g_object_unref (enumerator);
2378
brasero_io_xfer_start (BraseroIO *self,
2379
GCancellable *cancel,
2380
BraseroIOXferData *data,
2386
/* retrieve some information about the file we have to copy */
2387
file = g_file_new_for_uri (data->count.job.uri);
2388
data->info = g_file_query_info (file,
2389
G_FILE_ATTRIBUTE_STANDARD_TYPE","
2390
G_FILE_ATTRIBUTE_STANDARD_SIZE,
2391
G_FILE_QUERY_INFO_NONE, /* follow symlinks by default*/
2394
if (!data->info || error) {
2395
g_object_unref (file);
2399
g_file_info_set_attribute_string (data->info,
2400
BRASERO_IO_XFER_DESTINATION,
2403
/* see if we should explore it beforehand to report progress */
2404
if (data->count.job.base->progress) {
2405
data->count.files_num = 1;
2406
if (g_file_info_get_file_type (data->info) == G_FILE_TYPE_REGULAR
2407
|| g_file_info_get_file_type (data->info) == G_FILE_TYPE_SYMBOLIC_LINK)
2408
brasero_io_get_file_count_process_file (self, cancel, &data->count, file, data->info);
2409
else if (g_file_info_get_file_type (data->info) == G_FILE_TYPE_DIRECTORY)
2410
brasero_io_get_file_count_process_directory (self, cancel, &data->count);
2413
/* start the downloading */
2414
if (g_file_info_get_file_type (data->info) == G_FILE_TYPE_DIRECTORY) {
2415
if (g_mkdir_with_parents (data->dest_path, 700)) {
2418
g_object_unref (file);
2422
BRASERO_ERROR_GENERAL,
2423
_("A directory could not be created (%s)"),
2424
g_strerror (errsv));
2428
if (data->count.job.options & BRASERO_IO_INFO_RECURSIVE)
2429
brasero_io_xfer_recursive_thread (data,
2435
else if (g_file_info_get_file_type (data->info) == G_FILE_TYPE_REGULAR
2436
|| g_file_info_get_file_type (data->info) == G_FILE_TYPE_SYMBOLIC_LINK)
2437
result = brasero_io_xfer_file_thread (data,
2443
g_object_unref (file);
2448
brasero_io_xfer_get_progress_cb (BraseroIOJob *job,
2449
BraseroIOJobProgress *progress)
2451
BraseroIOXferData *data = (BraseroIOXferData *) job;
2453
if (progress->current)
2454
g_free (progress->current);
2456
g_mutex_lock (data->current_lock);
2457
progress->current = g_file_get_basename (data->current);
2458
g_mutex_unlock (data->current_lock);
2460
progress->total_b = data->count.total_b;
2461
progress->read_b = data->current_read_b + data->read_b;
2462
progress->files_num = data->count.files_num - data->count.files_invalid;
2465
static BraseroAsyncTaskResult
2466
brasero_io_xfer_thread (BraseroAsyncTaskManager *manager,
2467
GCancellable *cancel,
2468
gpointer callback_data)
2470
BraseroIOXferPair *pair;
2471
BraseroIOXferData *data = callback_data;
2474
GError *error = NULL;
2476
brasero_io_job_progress_report_start (BRASERO_IO (manager),
2478
brasero_io_xfer_get_progress_cb);
2480
if (!brasero_io_xfer_start (BRASERO_IO (manager), cancel, data, &error)) {
2481
brasero_io_return_result (BRASERO_IO (manager),
2482
data->count.job.base,
2483
data->count.job.uri,
2486
data->count.job.callback_data);
2487
return BRASERO_ASYNC_TASK_FINISHED;
2491
return BRASERO_ASYNC_TASK_RESCHEDULE;
2493
brasero_io_return_result (BRASERO_IO (manager),
2494
data->count.job.base,
2495
data->count.job.uri,
2498
data->count.job.callback_data);
2500
return BRASERO_ASYNC_TASK_FINISHED;
2503
/* If there is a progress callback, retrieve the size of all the data. */
2504
if (data->count.children) {
2505
brasero_io_get_file_count_process_directory (BRASERO_IO (manager), cancel, &data->count);
2506
return BRASERO_ASYNC_TASK_RESCHEDULE;
2509
pair = data->pairs->data;
2510
data->pairs = g_slist_remove (data->pairs, pair);
2512
brasero_io_xfer_recursive_thread (data,
2518
brasero_io_xfer_pair_free (pair);
2521
return BRASERO_ASYNC_TASK_RESCHEDULE;
2523
brasero_io_return_result (BRASERO_IO (manager),
2524
data->count.job.base,
2525
data->count.job.uri,
2528
data->count.job.callback_data);
2531
return BRASERO_ASYNC_TASK_FINISHED;
2534
static const BraseroAsyncTaskType xfer_type = {
2535
brasero_io_xfer_thread,
2536
brasero_io_xfer_destroy
2540
brasero_io_xfer (BraseroIO *self,
2542
const gchar *dest_path,
2543
const BraseroIOJobBase *base,
2544
BraseroIOFlags options,
2547
BraseroIOXferData *data;
2548
BraseroIOResultCallbackData *callback_data = NULL;
2551
callback_data = g_new0 (BraseroIOResultCallbackData, 1);
2552
callback_data->callback_data = user_data;
2555
data = g_new0 (BraseroIOXferData, 1);
2556
data->dest_path = g_strdup (dest_path);
2557
data->current_lock = g_mutex_new ();
2559
brasero_io_set_job (BRASERO_IO_JOB (data),
2565
brasero_io_push_job (self, BRASERO_IO_JOB (data), &xfer_type);
2569
brasero_io_cancel_result (BraseroIO *self,
2570
BraseroIOJobResult *result)
2572
BraseroIOResultCallbackData *data;
2573
BraseroIOPrivate *priv;
2575
priv = BRASERO_IO_PRIVATE (self);
2577
g_mutex_lock (priv->lock);
2578
priv->results = g_slist_remove (priv->results, result);
2579
g_mutex_unlock (priv->lock);
2581
data = result->callback_data;
2582
brasero_io_unref_result_callback_data (data,
2583
result->base->object,
2584
result->base->destroy,
2586
brasero_io_job_result_free (result);
2590
brasero_io_cancel_tasks_by_base_cb (BraseroAsyncTaskManager *manager,
2591
gpointer callback_data,
2594
BraseroIOJob *job = callback_data;
2595
BraseroIOJobBase *base = user_data;
2597
if (job->base != base)
2604
brasero_io_cancel_by_base (BraseroIO *self,
2605
BraseroIOJobBase *base)
2609
BraseroIOPrivate *priv;
2611
priv = BRASERO_IO_PRIVATE (self);
2613
brasero_async_task_manager_foreach_unprocessed_remove (BRASERO_ASYNC_TASK_MANAGER (self),
2614
brasero_io_cancel_tasks_by_base_cb,
2617
brasero_async_task_manager_foreach_active_remove (BRASERO_ASYNC_TASK_MANAGER (self),
2618
brasero_io_cancel_tasks_by_base_cb,
2621
/* do it afterwards in case some results slipped through */
2622
for (iter = priv->results; iter; iter = next) {
2623
BraseroIOJobResult *result;
2625
result = iter->data;
2628
if (result->base != base)
2631
brasero_io_cancel_result (self, result);
2636
brasero_io_cancel_tasks_by_data_cb (BraseroAsyncTaskManager *manager,
2637
gpointer callback_data,
2640
BraseroIOJob *job = callback_data;
2642
if (job->callback_data && job->callback_data->callback_data != user_data)
2649
brasero_io_cancel_by_data (BraseroIO *self,
2650
gpointer callback_data)
2654
BraseroIOPrivate *priv;
2656
priv = BRASERO_IO_PRIVATE (self);
2658
brasero_async_task_manager_foreach_unprocessed_remove (BRASERO_ASYNC_TASK_MANAGER (self),
2659
brasero_io_cancel_tasks_by_data_cb,
2662
brasero_async_task_manager_foreach_active_remove (BRASERO_ASYNC_TASK_MANAGER (self),
2663
brasero_io_cancel_tasks_by_data_cb,
2666
/* do it afterwards in case some results slipped through */
2667
for (iter = priv->results; iter; iter = next) {
2668
BraseroIOJobResult *result;
2670
result = iter->data;
2673
if (result->callback_data != callback_data)
2676
brasero_io_cancel_result (self, result);
2680
struct _BraseroIOJobCompareData {
2681
BraseroIOCompareCallback func;
2682
const BraseroIOJobBase *base;
2685
typedef struct _BraseroIOJobCompareData BraseroIOJobCompareData;
2688
brasero_io_compare_unprocessed_task (BraseroAsyncTaskManager *manager,
2690
gpointer callback_data)
2692
BraseroIOJob *job = task;
2693
BraseroIOJobCompareData *data = callback_data;
2695
if (job->base == data->base)
2698
if (!job->callback_data)
2701
return data->func (job->callback_data->callback_data, data->user_data);
2705
brasero_io_find_urgent (BraseroIO *self,
2706
const BraseroIOJobBase *base,
2707
BraseroIOCompareCallback callback,
2710
BraseroIOJobCompareData callback_data;
2712
callback_data.func = callback;
2713
callback_data.base = base;
2714
callback_data.user_data = user_data;
2716
brasero_async_task_manager_find_urgent_task (BRASERO_ASYNC_TASK_MANAGER (self),
2717
brasero_io_compare_unprocessed_task,
2723
brasero_io_register (GObject *object,
2724
BraseroIOResultCallback callback,
2725
BraseroIODestroyCallback destroy,
2726
BraseroIOProgressCallback progress)
2728
BraseroIOJobBase *base;
2730
base = g_new0 (BraseroIOJobBase, 1);
2731
base->object = object;
2732
base->callback = callback;
2733
base->destroy = destroy;
2734
base->progress = progress;
2740
brasero_io_init (BraseroIO *object)
2742
BraseroIOPrivate *priv;
2743
BraseroMetadata *metadata;
2744
priv = BRASERO_IO_PRIVATE (object);
2746
priv->lock = g_mutex_new ();
2747
priv->lock_metadata = g_mutex_new ();
2749
priv->meta_buffer = g_queue_new ();
2751
/* create metadatas now since it doesn't work well when it's created in
2753
metadata = brasero_metadata_new ();
2754
priv->metadatas = g_slist_prepend (priv->metadatas, metadata);
2755
metadata = brasero_metadata_new ();
2756
priv->metadatas = g_slist_prepend (priv->metadatas, metadata);
2760
brasero_io_free_async_queue (BraseroAsyncTaskManager *manager,
2761
gpointer callback_data,
2764
BraseroIOJob *job = callback_data;
2766
brasero_io_job_free (BRASERO_IO (manager), TRUE, job);
2771
brasero_io_finalize (GObject *object)
2773
BraseroIOPrivate *priv;
2776
priv = BRASERO_IO_PRIVATE (object);
2778
brasero_async_task_manager_foreach_unprocessed_remove (BRASERO_ASYNC_TASK_MANAGER (object),
2779
brasero_io_free_async_queue,
2782
brasero_async_task_manager_foreach_active_remove (BRASERO_ASYNC_TASK_MANAGER (object),
2783
brasero_io_free_async_queue,
2786
g_slist_foreach (priv->metadatas, (GFunc) g_object_unref, NULL);
2787
g_slist_free (priv->metadatas);
2788
priv->metadatas = NULL;
2790
if (priv->meta_buffer) {
2791
BraseroIOMetadataCached *cached;
2793
while ((cached = g_queue_pop_head (priv->meta_buffer)) != NULL)
2794
brasero_io_metadata_cached_free (cached);
2796
g_queue_free (priv->meta_buffer);
2797
priv->meta_buffer = NULL;
2800
if (priv->results_id) {
2801
g_source_remove (priv->results_id);
2802
priv->results_id = 0;
2805
for (iter = priv->results; iter; iter = iter->next) {
2806
BraseroIOJobResult *result;
2808
result = iter->data;
2809
brasero_io_job_result_free (result);
2811
g_slist_free (priv->results);
2812
priv->results = NULL;
2814
if (priv->progress_id) {
2815
g_source_remove (priv->progress_id);
2816
priv->progress_id = 0;
2819
if (priv->progress) {
2820
g_slist_foreach (priv->progress, (GFunc) g_free, NULL);
2821
g_slist_free (priv->progress);
2822
priv->progress = NULL;
2826
g_mutex_free (priv->lock);
2830
if (priv->lock_metadata) {
2831
g_mutex_free (priv->lock_metadata);
2832
priv->lock_metadata = NULL;
2835
if (priv->mounted) {
2838
/* unmount all volumes we mounted ourselves */
2839
for (iter = priv->mounted; iter; iter = iter->next) {
2844
BRASERO_BURN_LOG ("Unmountin volume");
2845
g_mount_unmount (mount,
2846
G_MOUNT_UNMOUNT_NONE,
2850
g_object_unref (mount);
2854
G_OBJECT_CLASS (brasero_io_parent_class)->finalize (object);
2858
brasero_io_class_init (BraseroIOClass *klass)
2860
GObjectClass* object_class = G_OBJECT_CLASS (klass);
2862
g_type_class_add_private (klass, sizeof (BraseroIOPrivate));
2864
object_class->finalize = brasero_io_finalize;
2867
static BraseroIO *singleton = NULL;
2870
brasero_io_last_reference_cb (gpointer null_data,
2872
gboolean is_last_ref)
2876
g_object_remove_toggle_ref (object,
2877
brasero_io_last_reference_cb,
2883
brasero_io_get_default ()
2886
g_object_ref (singleton);
2890
singleton = g_object_new (BRASERO_TYPE_IO, NULL);
2891
g_object_add_toggle_ref (G_OBJECT (singleton),
2892
brasero_io_last_reference_cb,