1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3 * Libbrasero-burn
4 * Copyright (C) Philippe Rouquier 2005-2009 <bonfire-app@wanadoo.fr>
5 *
6 * Libbrasero-burn 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.
10 *
11 * The Libbrasero-burn authors hereby grant permission for non-GPL compatible
12 * GStreamer plugins to be used and distributed together with GStreamer
13 * and Libbrasero-burn. This permission is above and beyond the permissions granted
14 * by the GPL license by which Libbrasero-burn is covered. If you modify this code
15 * you may extend this exception to your version of the code, but you are not
16 * obligated to do so. If you do not wish to do so, delete this exception
17 * statement from your version.
18 *
19 * Libbrasero-burn is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to:
26 * The Free Software Foundation, Inc.,
27 * 51 Franklin Street, Fifth Floor
28 * Boston, MA 02110-1301, USA.
29 */
30
31 #ifdef HAVE_CONFIG_H
32 # include <config.h>
33 #endif
34
35 #include <stdlib.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <time.h>
40
41 #include <glib.h>
42 #include <glib-object.h>
43 #include <glib/gi18n-lib.h>
44 #include <glib/gstdio.h>
45 #include <gmodule.h>
46
47 #include "brasero-units.h"
48
49 #include "burn-debug.h"
50 #include "burn-job.h"
51 #include "burn-process.h"
52 #include "brasero-plugin-registration.h"
53 #include "burn-cdrtools.h"
54 #include "brasero-track-disc.h"
55 #include "brasero-track-stream.h"
56
57 #define BRASERO_TYPE_CDDA2WAV (brasero_cdda2wav_get_type ())
58 #define BRASERO_CDDA2WAV(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), BRASERO_TYPE_CDDA2WAV, BraseroCdda2wav))
59 #define BRASERO_CDDA2WAV_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), BRASERO_TYPE_CDDA2WAV, BraseroCdda2wavClass))
60 #define BRASERO_IS_CDDA2WAV(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), BRASERO_TYPE_CDDA2WAV))
61 #define BRASERO_IS_CDDA2WAV_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), BRASERO_TYPE_CDDA2WAV))
62 #define BRASERO_CDDA2WAV_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), BRASERO_TYPE_CDDA2WAV, BraseroCdda2wavClass))
63
64 BRASERO_PLUGIN_BOILERPLATE (BraseroCdda2wav, brasero_cdda2wav, BRASERO_TYPE_PROCESS, BraseroProcess);
65
66 struct _BraseroCdda2wavPrivate {
67 gchar *file_pattern;
68
69 guint track_num;
70 guint track_nb;
71
72 guint is_inf :1;
73 };
74 typedef struct _BraseroCdda2wavPrivate BraseroCdda2wavPrivate;
75
76 #define BRASERO_CDDA2WAV_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), BRASERO_TYPE_CDDA2WAV, BraseroCdda2wavPrivate))
77 static GObjectClass *parent_class = NULL;
78
79
80 static BraseroBurnResult
brasero_cdda2wav_post(BraseroJob * job)81 brasero_cdda2wav_post (BraseroJob *job)
82 {
83 BraseroCdda2wavPrivate *priv;
84 BraseroMedium *medium;
85 BraseroJobAction action;
86 BraseroDrive *drive;
87 BraseroTrack *track;
88 int track_num;
89 int i;
90
91 priv = BRASERO_CDDA2WAV_PRIVATE (job);
92
93 brasero_job_get_action (job, &action);
94 if (action == BRASERO_JOB_ACTION_SIZE)
95 return BRASERO_BURN_OK;
96
97 /* we add the tracks */
98 track = NULL;
99 brasero_job_get_current_track (job, &track);
100
101 drive = brasero_track_disc_get_drive (BRASERO_TRACK_DISC (track));
102 medium = brasero_drive_get_medium (drive);
103
104 track_num = brasero_medium_get_track_num (medium);
105 for (i = 0; i < track_num; i ++) {
106 BraseroTrackStream *track_stream;
107 goffset block_num = 0;
108
109 brasero_medium_get_track_space (medium, i + 1, NULL, &block_num);
110 track_stream = brasero_track_stream_new ();
111
112 brasero_track_stream_set_format (track_stream,
113 BRASERO_AUDIO_FORMAT_RAW|
114 BRASERO_METADATA_INFO);
115
116 BRASERO_JOB_LOG (job, "Adding new audio track of size %" G_GOFFSET_FORMAT, BRASERO_BYTES_TO_DURATION (block_num * 2352));
117
118 /* either add .inf or .cdr files */
119 if (!priv->is_inf) {
120 gchar *uri;
121 gchar *filename;
122
123 if (track_num == 1)
124 filename = g_strdup_printf ("%s.cdr", priv->file_pattern);
125 else
126 filename = g_strdup_printf ("%s_%02i.cdr", priv->file_pattern, i + 1);
127
128 uri = g_filename_to_uri (filename, NULL, NULL);
129 g_free (filename);
130
131 brasero_track_stream_set_source (track_stream, uri);
132 g_free (uri);
133
134 /* signal to cdrecord that we have an .inf file */
135 if (i != 0)
136 filename = g_strdup_printf ("%s_%02i.inf", priv->file_pattern, i);
137 else
138 filename = g_strdup_printf ("%s.inf", priv->file_pattern);
139
140 brasero_track_tag_add_string (BRASERO_TRACK (track_stream),
141 BRASERO_CDRTOOLS_TRACK_INF_FILE,
142 filename);
143 g_free (filename);
144 }
145
146 /* Always set the boundaries after the source as
147 * brasero_track_stream_set_source () resets the length */
148 brasero_track_stream_set_boundaries (track_stream,
149 0,
150 BRASERO_BYTES_TO_DURATION (block_num * 2352),
151 0);
152 brasero_job_add_track (job, BRASERO_TRACK (track_stream));
153 g_object_unref (track_stream);
154 }
155
156 return brasero_job_finished_session (job);
157 }
158
159 static gboolean
brasero_cdda2wav_get_output_filename_pattern(BraseroCdda2wav * cdda2wav,GError ** error)160 brasero_cdda2wav_get_output_filename_pattern (BraseroCdda2wav *cdda2wav,
161 GError **error)
162 {
163 gchar *path;
164 gchar *file_pattern;
165 BraseroCdda2wavPrivate *priv;
166
167 priv = BRASERO_CDDA2WAV_PRIVATE (cdda2wav);
168
169 if (priv->file_pattern) {
170 g_free (priv->file_pattern);
171 priv->file_pattern = NULL;
172 }
173
174 /* Create a tmp directory so cdda2wav can
175 * put all its stuff in there */
176 path = NULL;
177 brasero_job_get_tmp_dir (BRASERO_JOB (cdda2wav), &path, error);
178 if (!path)
179 return FALSE;
180
181 file_pattern = g_strdup_printf ("%s/cd_file", path);
182 g_free (path);
183
184 /* NOTE: this file pattern is used to
185 * name all wav and inf files. It is followed
186 * by underscore/number of the track/extension */
187
188 priv->file_pattern = file_pattern;
189 return TRUE;
190 }
191
192 static BraseroBurnResult
brasero_cdda2wav_read_stderr(BraseroProcess * process,const gchar * line)193 brasero_cdda2wav_read_stderr (BraseroProcess *process, const gchar *line)
194 {
195 int num;
196 BraseroCdda2wav *cdda2wav;
197 BraseroCdda2wavPrivate *priv;
198
199 cdda2wav = BRASERO_CDDA2WAV (process);
200 priv = BRASERO_CDDA2WAV_PRIVATE (process);
201
202 if (sscanf (line, "100%% track %d '%*s' recorded successfully", &num) == 1) {
203 gchar *string;
204
205 priv->track_nb = num;
206 string = g_strdup_printf (_("Copying audio track %02d"), priv->track_nb + 1);
207 brasero_job_set_current_action (BRASERO_JOB (process),
208 BRASERO_BURN_ACTION_DRIVE_COPY,
209 string,
210 TRUE);
211 g_free (string);
212 }
213 else if (strstr (line, "percent_done:")) {
214 gchar *string;
215
216 string = g_strdup_printf (_("Copying audio track %02d"), 1);
217 brasero_job_set_current_action (BRASERO_JOB (process),
218 BRASERO_BURN_ACTION_DRIVE_COPY,
219 string,
220 TRUE);
221 g_free (string);
222 }
223 /* we have to do this otherwise with sscanf it will
224 * match every time it begins with a number */
225 else if (strchr (line, '%') && sscanf (line, " %d%%", &num) == 1) {
226 gdouble fraction;
227
228 fraction = (gdouble) num / (gdouble) 100.0;
229 fraction = ((gdouble) priv->track_nb + fraction) / (gdouble) priv->track_num;
230 brasero_job_set_progress (BRASERO_JOB (cdda2wav), fraction);
231 brasero_job_start_progress (BRASERO_JOB (process), FALSE);
232 }
233
234 return BRASERO_BURN_OK;
235 }
236
237 static BraseroBurnResult
brasero_cdda2wav_set_argv_image(BraseroCdda2wav * cdda2wav,GPtrArray * argv,GError ** error)238 brasero_cdda2wav_set_argv_image (BraseroCdda2wav *cdda2wav,
239 GPtrArray *argv,
240 GError **error)
241 {
242 BraseroCdda2wavPrivate *priv;
243 int fd_out;
244
245 priv = BRASERO_CDDA2WAV_PRIVATE (cdda2wav);
246
247 /* We want raw output */
248 g_ptr_array_add (argv, g_strdup ("output-format=cdr"));
249
250 /* we want all tracks */
251 g_ptr_array_add (argv, g_strdup ("-B"));
252
253 priv->is_inf = FALSE;
254
255 if (brasero_job_get_fd_out (BRASERO_JOB (cdda2wav), &fd_out) == BRASERO_BURN_OK) {
256 /* On the fly copying */
257 g_ptr_array_add (argv, g_strdup ("-"));
258 }
259 else {
260 if (!brasero_cdda2wav_get_output_filename_pattern (cdda2wav, error))
261 return BRASERO_BURN_ERR;
262
263 g_ptr_array_add (argv, g_strdup (priv->file_pattern));
264
265 brasero_job_set_current_action (BRASERO_JOB (cdda2wav),
266 BRASERO_BURN_ACTION_DRIVE_COPY,
267 _("Preparing to copy audio disc"),
268 FALSE);
269 }
270
271 return BRASERO_BURN_OK;
272 }
273
274 static BraseroBurnResult
brasero_cdda2wav_set_argv_size(BraseroCdda2wav * cdda2wav,GPtrArray * argv,GError ** error)275 brasero_cdda2wav_set_argv_size (BraseroCdda2wav *cdda2wav,
276 GPtrArray *argv,
277 GError **error)
278 {
279 BraseroCdda2wavPrivate *priv;
280 BraseroMedium *medium;
281 BraseroTrack *track;
282 BraseroDrive *drive;
283 goffset medium_len;
284 int i;
285
286 priv = BRASERO_CDDA2WAV_PRIVATE (cdda2wav);
287
288 /* we add the tracks */
289 medium_len = 0;
290 track = NULL;
291 brasero_job_get_current_track (BRASERO_JOB (cdda2wav), &track);
292
293 drive = brasero_track_disc_get_drive (BRASERO_TRACK_DISC (track));
294 medium = brasero_drive_get_medium (drive);
295
296 priv->track_num = brasero_medium_get_track_num (medium);
297 for (i = 0; i < priv->track_num; i ++) {
298 goffset len = 0;
299
300 brasero_medium_get_track_space (medium, i, NULL, &len);
301 medium_len += len;
302 }
303 brasero_job_set_output_size_for_current_track (BRASERO_JOB (cdda2wav), medium_len, medium_len * 2352);
304
305 /* if there isn't any output file then that
306 * means we have to generate all the
307 * .inf files for cdrecord. */
308 if (brasero_job_get_audio_output (BRASERO_JOB (cdda2wav), NULL) != BRASERO_BURN_OK)
309 return BRASERO_BURN_NOT_RUNNING;
310
311 /* we want all tracks */
312 g_ptr_array_add (argv, g_strdup ("-B"));
313
314 /* since we're running for an on-the-fly burning
315 * we only want the .inf files */
316 g_ptr_array_add (argv, g_strdup ("-J"));
317
318 if (!brasero_cdda2wav_get_output_filename_pattern (cdda2wav, error))
319 return BRASERO_BURN_ERR;
320
321 g_ptr_array_add (argv, g_strdup (priv->file_pattern));
322
323 priv->is_inf = TRUE;
324
325 return BRASERO_BURN_OK;
326 }
327
328 static BraseroBurnResult
brasero_cdda2wav_set_argv(BraseroProcess * process,GPtrArray * argv,GError ** error)329 brasero_cdda2wav_set_argv (BraseroProcess *process,
330 GPtrArray *argv,
331 GError **error)
332 {
333 BraseroDrive *drive;
334 const gchar *device;
335 BraseroTrack *track;
336 BraseroJobAction action;
337 BraseroBurnResult result;
338 BraseroCdda2wav *cdda2wav;
339
340 cdda2wav = BRASERO_CDDA2WAV (process);
341
342 g_ptr_array_add (argv, g_strdup ("cdda2wav"));
343
344 /* Add the device path */
345 track = NULL;
346 result = brasero_job_get_current_track (BRASERO_JOB (process), &track);
347 if (!track)
348 return result;
349
350 drive = brasero_track_disc_get_drive (BRASERO_TRACK_DISC (track));
351 device = brasero_drive_get_device (drive);
352 g_ptr_array_add (argv, g_strdup_printf ("dev=%s", device));
353
354 /* Have it talking */
355 g_ptr_array_add (argv, g_strdup ("-v255"));
356
357 brasero_job_get_action (BRASERO_JOB (cdda2wav), &action);
358 if (action == BRASERO_JOB_ACTION_SIZE)
359 result = brasero_cdda2wav_set_argv_size (cdda2wav, argv, error);
360 else if (action == BRASERO_JOB_ACTION_IMAGE)
361 result = brasero_cdda2wav_set_argv_image (cdda2wav, argv, error);
362 else
363 BRASERO_JOB_NOT_SUPPORTED (cdda2wav);
364
365 return result;
366 }
367
368 static void
brasero_cdda2wav_class_init(BraseroCdda2wavClass * klass)369 brasero_cdda2wav_class_init (BraseroCdda2wavClass *klass)
370 {
371 GObjectClass *object_class = G_OBJECT_CLASS (klass);
372 BraseroProcessClass *process_class = BRASERO_PROCESS_CLASS (klass);
373
374 g_type_class_add_private (klass, sizeof (BraseroCdda2wavPrivate));
375
376 parent_class = g_type_class_peek_parent(klass);
377 object_class->finalize = brasero_cdda2wav_finalize;
378
379 process_class->stderr_func = brasero_cdda2wav_read_stderr;
380 process_class->set_argv = brasero_cdda2wav_set_argv;
381 process_class->post = brasero_cdda2wav_post;
382 }
383
384 static void
brasero_cdda2wav_init(BraseroCdda2wav * obj)385 brasero_cdda2wav_init (BraseroCdda2wav *obj)
386 { }
387
388 static void
brasero_cdda2wav_finalize(GObject * object)389 brasero_cdda2wav_finalize (GObject *object)
390 {
391 BraseroCdda2wavPrivate *priv;
392
393 priv = BRASERO_CDDA2WAV_PRIVATE (object);
394
395 if (priv->file_pattern) {
396 g_free (priv->file_pattern);
397 priv->file_pattern = NULL;
398 }
399 G_OBJECT_CLASS (parent_class)->finalize (object);
400 }
401
402 static void
brasero_cdda2wav_export_caps(BraseroPlugin * plugin)403 brasero_cdda2wav_export_caps (BraseroPlugin *plugin)
404 {
405 GSList *output;
406 GSList *input;
407
408 brasero_plugin_define (plugin,
409 "cdda2wav",
410 NULL,
411 _("Copy tracks from an audio CD with all associated information"),
412 "Philippe Rouquier",
413 1);
414
415 output = brasero_caps_audio_new (BRASERO_PLUGIN_IO_ACCEPT_FILE /*|BRASERO_PLUGIN_IO_ACCEPT_PIPE*/, /* Keep on the fly on hold until it gets proper testing */
416 BRASERO_AUDIO_FORMAT_RAW|
417 BRASERO_METADATA_INFO);
418
419 input = brasero_caps_disc_new (BRASERO_MEDIUM_CDR|
420 BRASERO_MEDIUM_CDRW|
421 BRASERO_MEDIUM_CDROM|
422 BRASERO_MEDIUM_CLOSED|
423 BRASERO_MEDIUM_APPENDABLE|
424 BRASERO_MEDIUM_HAS_AUDIO);
425
426 brasero_plugin_link_caps (plugin, output, input);
427 g_slist_free (output);
428 g_slist_free (input);
429
430 brasero_plugin_register_group (plugin, _(CDRTOOLS_DESCRIPTION));
431 }
432
433 G_MODULE_EXPORT void
brasero_plugin_check_config(BraseroPlugin * plugin)434 brasero_plugin_check_config (BraseroPlugin *plugin)
435 {
436 gchar *prog_path;
437
438 /* Just check that the program is in the path and executable. */
439 prog_path = g_find_program_in_path ("cdda2wav");
440 if (!prog_path) {
441 brasero_plugin_add_error (plugin,
442 BRASERO_PLUGIN_ERROR_MISSING_APP,
443 "cdda2wav");
444 return;
445 }
446
447 if (!g_file_test (prog_path, G_FILE_TEST_IS_EXECUTABLE)) {
448 g_free (prog_path);
449 brasero_plugin_add_error (plugin,
450 BRASERO_PLUGIN_ERROR_MISSING_APP,
451 "cdda2wav");
452 return;
453 }
454 g_free (prog_path);
455
456 /* This is what it should be. Now, as I did not write and icedax plugin
457 * the above is enough so that cdda2wav can use a symlink to icedax.
458 * As for the version checking, it becomes impossible given that with
459 * icedax the string would not start with cdda2wav. So ... */
460 /*
461 gint version [3] = { 2, 0, -1};
462 brasero_plugin_test_app (plugin,
463 "cdda2wav",
464 "--version",
465 "cdda2wav %d.%d",
466 version);
467 */
468 }
469