1
# -*- coding: utf-8 -*-
5
# Copyright © 2009 Ben Finney <ben+python@benfinney.id.au>.
6
# Copyright © 2003–2009 Gaetano Paolone <bigpaul@hacknight.org>.
8
# This program is free software; you can redistribute it and/or
9
# modify it under the terms of the GNU General Public License
10
# as published by the Free Software Foundation; either version 2
11
# of the License, or (at your option) any later version.
13
# This program is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
# GNU General Public License for more details.
18
# You should have received a copy of the GNU General Public License
19
# along with this program; if not, write to the Free Software
20
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
38
import burnlib.version
41
from configure import config
45
gettext.bindtextdomain('burn', '/usr/share/locale/it/LC_MESSAGES/')
46
gettext.textdomain('burn')
50
class ContentMode(object):
51
""" Modes for content handling. """
53
(data, iso, copy, audio) = (object() for n in range(4))
63
def du(dirname, path_du, follow_symlink=False):
64
#is like du. goes through dirname
65
#and subdirs and append every file size to path_du
66
"""Goes deeply through path in order to calculate path's disk usage."""
67
for root, dirs, files in os.walk(dirname):
68
for file_name in files:
69
if os.path.exists(os.path.realpath(os.path.join(root, file_name))):
70
path_du += os.path.getsize(os.path.join(root, file_name))
72
for directory in dirs:
73
print os.path.join(root, directory)
74
#os.walk does not follow symlink directories.
75
#Next line will allow it.
76
if os.path.islink(os.path.join(root, directory)):
78
os.path.realpath(os.path.join(root, directory)), path_du,
83
def check_main_option(opt):
84
"""Checks first argument."""
92
'Invalid syntax. First argument should be a main mode.'
93
' See \'burn -h\' for more info.')
97
def check_media_empty():
98
""" Return True if media device is empty. """
100
config.get('executables', 'cdrdao'), "disk-info",
101
"--device", config.get('CD-writer', 'device'),
102
"--driver", config.get('CD-writer', 'driver'),
104
command_process = subprocess.Popen(
105
command_args, close_fds=True,
106
stdout=subprocess.PIPE, stderr=open(os.devnull))
107
for line in command_process.stdout:
108
if line.startswith('CD-R empty'):
109
if line.split(':')[1].strip() == 'yes':
116
"""Prints an error using why as argument."""
117
print _('Error. '), why
120
def err_nf(dir_file):
121
"""Not found error message."""
122
print _('Error. '), dir_file, _(' not found.')
125
def get_list_from_file(path):
126
"""extract a list of paths from a file"""
130
line.strip() for line in in_file)
131
if os.path.isfile(path)]
135
def get_media_capacity():
136
""" Get total capacity of media device. """
138
config.get('executables', 'cdrdao'), "disk-info",
139
"--device", config.get('CD-writer', 'device'),
140
"--driver", config.get('CD-writer', 'driver'),
142
command_process = subprocess.Popen(
143
command_args, close_fds=True,
144
stdout=subprocess.PIPE, stderr=open(os.devnull))
145
for line in command_process.stdout:
146
if line.startswith('Total Capacity'):
147
if line.split(':')[1].strip() == 'n/a':
149
return line.split()[6].split('/')[0]
153
def prog_intro(mode=None):
154
""" Output program introduction message for specified mode. """
156
if config.getboolean('general', 'ask_root'):
157
if not pwd.getpwuid(os.geteuid())[0] == "root":
158
if not console.ask_yesno(_(
159
'You are not superuser (root).'
160
' Do you still want to continue'), True):
163
'Burn v.%(version)s '
164
'Written by %(author_name)s.') % vars(burnlib.version)
165
print _('Burn until recorded, now!')
167
'This software comes with absolutely no warranty!'
168
' Use at your own risk!')
169
print _('Burn is free software.')
170
print _('See software updates at <URL:%(_url)s>.') % vars(burnlib)
173
mode_title = ContentMode.titles.get(mode)
174
if mode_title is not None:
175
print mode_title + "..."
179
def show_examples(option, opt, value, parser):
180
"""Show examples for quick startup"""
182
print "# burn -D -p /etc/"
184
' Creates a CD/DVD with /etc/ contents. (you will find files'
185
' and directories contained in /etc in CD\'s root)')
186
print "# burn -D -p /home/bigpaul/video/summer_2003/spain.tar.gz"
187
print _(' Creates a CD/DVD with spain.tar.gz in CD\'s root')
188
print "# burn -D -r /etc/"
190
' Creates a CD/DVD containing the whole /etc/ directory.'
191
' (-r preserves path)')
192
print "# burn -D -c /mail_2003 /home/bigpaul/Mail -p /boot/vmli*"
194
' Creates a CD/DVD containing the whole /home/bigpaul/Mail'
195
' renamed into /mail_2003. (-c changes path name).'
196
' This command also adds in CD\'s root every vmli* file'
197
' in /boot/ directory')
198
print "# burn -I -n image.iso"
199
print _(' Burns image.iso')
201
print _(' Copy CDs (disk at once).')
202
print "# burn -A -a *.wav"
203
print _(' Creates an Audio CD. Tracks come from wav files')
204
print "# burn -A -a *.mp3"
205
print _(' Creates an Audio CD. Tracks come from mp3 files')
206
print "# burn -A -a *.ogg"
207
print _(' Creates an Audio CD. Tracks come from Ogg Vorbis files')
208
print "# burn -A -a *.mp3 file.ogg track01.wav"
209
print _(' Creates an Audio CD. Tracks come from .wav, .ogg, .mp3 files')
214
def varargs(option, opt, value, parser):
215
"""Callback function to manage multiple arguments (or shell expansion)"""
223
if ((arg[:2] == "--" and len(arg) > 2) or
224
(arg[:1] == "-" and len(arg) > 1 and arg[1] != "-")):
227
'%(option)s is an option that takes one or more arguments.'
228
' So you can\'t put %(arg)s after it') % vars()
235
setattr(parser.values, option.dest, value)
242
""" Set up a new instance. """
244
self.genisoimage_args = []
245
self.windows = config.getboolean('ISO', 'windows_read')
246
self.tempdir = config.get('ISO', 'tempdir')
247
self.image_name = config.get('ISO', 'image')
248
self.mount_dir = config.get('ISO', 'mount_dir')
249
self.dest = os.path.normpath(self.tempdir + self.image_name)
252
""" Execute data track recording command. """
253
print _('Creating temporary image: '), self.dest
254
pbar = console.ProgressBar(width=30)
255
command_process = subprocess.Popen(
256
self.genisoimage_args, close_fds=True,
257
stdin=subprocess.PIPE,
258
stdout=open(os.devnull), stderr=subprocess.PIPE)
260
for line in command_process.stderr:
261
if "done, estimate finish" in line:
262
progress = int(float(line.split()[0][:-1]))
263
pbar.amount = progress
264
pbar.update_display()
266
pbar.update_display()
269
"""remove the file"""
272
def ask_mount(self, image):
273
"""asks if user wants to mount image"""
274
if console.ask_yesno(_(
275
'Do you want to see image\'s contents before proceeding'
280
"""return free disk space"""
281
space = os.statvfs(self.tempdir)
282
return (long(space[statvfs.F_BAVAIL]) * \
283
long(space[statvfs.F_BSIZE]))/1048576
285
def mount(self, image):
286
"""mount image in self.mount_dir"""
287
mount_dir = self.mount_dir
288
if os.path.exists(mount_dir) and os.path.isdir(mount_dir):
289
command_args = ["mount", "-o", "loop", image, mount_dir]
290
if subprocess.call(command_args):
292
'Unable to mount %(image)s. Please check if you have'
293
' permissions to mount it on %(mount_dir)s') % vars()
295
self.ask_after_mount(self.mount_dir)
297
err(self.mount_dir + _(' is not valid as a mount point'))
299
command_args = ["umount", self.mount_dir]
300
subprocess.call(command_args)
302
def ask_after_mount(self, dirname):
303
"""Choose what to do with the mounted image..."""
305
'\n\nPlease choose:\n\n'
306
'1. Print every file with path\n'
307
'2. Print directory tree structure\n'
308
'3. Ok, finished... Let\'s burn\n'
310
mount_choice = raw_input(prompt)
311
if mount_choice in ('1'): #option num. 1 ---> every file
313
for root, dirs, files in os.walk(dirname):
314
for file_path in files:
315
print os.path.join(root, file_path)
316
self.ask_after_mount(dirname) #return True
317
if mount_choice in ('2'): #option num. 2 ---> tree structure
319
for root, dirs, files in os.walk(dirname):
320
for directory in dirs:
321
print os.path.join(root, directory)
322
self.ask_after_mount(dirname)
323
if mount_choice in ('3'): #option num. 3 ---> done
326
def ask_multisession(self):
327
"""Asks if user wants to add stuff to a multisession CD or if he wants
328
to create a new multisession CD from scratch."""
330
'\n\nPlease choose:\n\n'
331
'1. Create new multisession CD from a blank media\n'
332
'2. Append data to an existant multisession CD\n'
334
multisession_cd = raw_input(prompt)
335
if multisession_cd in ('1'): #option num. 1 new multisession
337
if multisession_cd in ('2'): #option num. 2 already multisession
340
def ask_remove(self):
341
"""asks to remove image"""
343
print self.dest, _(' is the image file created...')
344
if console.ask_yesno(_('Do you want me to remove it'), False):
345
print _('Removing '), self.dest, "..."
351
def first_ask_remove(self):
352
"""asks to remove image at the very beginning"""
355
'Warning. there is already a temporary image file'
356
' named %(dest)s.') % vars(self)
357
if console.ask_yesno(_('Do you want me to remove it'), False):
358
print _('Removing '), self.dest, "..."
368
def __init__(self, options):
369
""" Set up a new instance. """
371
self.wodim = config.get('executables', 'wodim')
372
self.cdrdao = config.get('executables', 'cdrdao')
373
self.speed = config.getint('CD-writer', 'speed')
374
self.device = config.get('CD-writer', 'device')
375
self.source_device = config.get('CD-reader', 'device')
376
self.driver = config.get('CD-writer', 'driver')
377
self.source_driver = config.get('CD-reader', 'driver')
378
self.burnfree = config.getboolean('CD-writer', 'burnfree')
380
self.content_mode = options.mode
381
self.multisession = options.multisession
382
self.simulate = options.simulate
383
self.eject = options.eject
387
def compute_media_size(self):
388
""" Get the storage capacity of the media. """
389
if config.getboolean('Media', 'media-check'):
390
empty = check_media_empty()
392
if not self.multisession:
393
print _('Error. Please insert a blank CD/DVD.')
395
self.wodim, "-eject",
396
"dev=%(device)s" % vars(self)]
397
subprocess.call(command_args, stderr=open(os.devnull))
400
self.size = get_media_capacity()
403
"Error. unknown media capacity. "
404
"Using configuration default.")
405
self.size = config.getint('Media', 'size')
407
self.size = config.getint('Media', 'size')
409
def size_compare(self, tobeburned, space_needed):
410
"""Checks free space for temporary files and CD oversize"""
411
free_disk_space = int(self.iso.freespace())
412
self.compute_media_size()
414
print _('To be burned: '), "\t\t\t", tobeburned / 1048576, "Mb"
415
print _('Disk space needed: '), "\t\t", space_needed/1048576, "Mb"
416
print _('Media capacity: '), "\t\t", self.size, "Mb"
417
if self.content_mode is not ContentMode.iso:
418
print _('Free disk space: '), "\t\t", free_disk_space, "Mb"
419
if space_needed == 0:
420
space_needed = tobeburned
421
if self.content_mode is not ContentMode.iso:
422
if (space_needed/1048576) > free_disk_space:
423
if self.content_mode is ContentMode.data:
424
print _('You do not have enough free disk space'), \
425
" (", free_disk_space, " Mb )", \
426
_('to create temporary image file '), \
427
"( ", tobeburned / 1048576, " Mb )"
428
elif self.content_mode is ContentMode.audio:
429
print _('You do not have enough free disk space'), \
430
" (", free_disk_space, " Mb )", \
431
_('to create temporary audio files '), \
432
"( ", tobeburned / 1048576, " Mb )"
434
if (tobeburned / 1048576) > int(self.size):
435
if not console.ask_yesno(_(
436
'It seems you are going to burn more than media\'s capacity.\n'
437
'Do you still want to continue'), False):
441
def make_command_args(self):
442
""" Generate command-line arguments for track recording. """
443
self.wodim_args = [self.wodim, "-v", "-pad"]
445
self.wodim_args.append("-dummy")
447
self.wodim_args.append("-eject")
449
self.wodim_args.append("speed=%(speed)s" % vars(self))
451
print _('no burning speed defined, using 2x')
452
self.wodim_args.append("speed=2")
454
self.wodim_args.append("dev=%(device)s" % vars(self))
456
print _('no device specified.')
459
self.wodim_args.append("driveropts=burnfree")
460
if self.multisession:
461
self.wodim_args.append("-multi")
462
if self.content_mode in [ContentMode.data, ContentMode.iso]:
463
self.wodim_args.extend(["-data", self.iso.dest])
466
""" Execute track recording command. """
468
if config.getboolean('general', 'pause'):
469
print _('Press a key to begin recording...')
471
print _('Please wait...')
472
subprocess.call(self.wodim_args)
474
def double_dao_create(self, command_args):
475
""" Execute disk-at-once copy with two drives (reader and writer). """
477
print _('Place the source CD in the CD drive')
478
print _('and place a blank media in the recording unit.')
479
print _('Press a key to begin on-the-fly copy')
481
subprocess.call(command_args)
483
def single_dao_create(self, command_args):
484
""" Execute disk-at-once copy with one drive. """
486
print _('Place source CD in the recording unit.')
487
print _('Press a key to begin reading...')
489
subprocess.call(command_args)
491
def another_copy(self):
492
"""burn image untill user says no"""
493
while console.ask_yesno(_(
494
'Do you want to use this image to make another copy now?'
496
self.make_command_args()
501
""" Program mainline procedure. """
503
usage = """%prog -MODE [general_option] [mode_option] ...
505
For quick start you can get common examples with:
509
parser = optparse.OptionParser(usage=usage)
512
"-e", "--examples", action="callback", callback=show_examples,
514
help=_('show examples for quick startup'))
517
mode_options = optparse.OptionGroup(
518
parser, _('Main burn MODES'),
519
_('They _have_ to be the first argument after program name'))
520
mode_options.add_option(
522
action="store_const", dest="mode", const=ContentMode.data,
523
help=_('creates a Data CD/DVD.'))
524
mode_options.add_option(
526
action="store_const", dest="mode", const=ContentMode.iso,
527
help=_('creates a CD/DVD from ISO.'))
528
mode_options.add_option(
530
action="store_const", dest="mode", const=ContentMode.copy,
531
help=_('copy CD/DVD.'))
532
mode_options.add_option(
534
action="store_const", dest="mode", const=ContentMode.audio,
535
help=_('creates an Audio CD from .wav, .mp3 and .ogg files.'))
538
general_option = optparse.OptionGroup(
539
parser, "General options",
541
'These options could be used for every burn mode'
542
' unless stated otherwise'))
544
general_option.add_option(
545
"-s", "--simulate", action="store_true", dest="simulate",
546
help=_('This option will perform a burn simulation.'))
547
general_option.add_option(
548
"-j", "--eject", action="store_true", dest="eject",
550
'This option will eject disk when burn process is over.'))
551
general_option.add_option(
552
"--dao", action="store_true", dest="dao",
554
'Enable disk at once. Enabling this option, you will not have'
555
' 2 seconds gap between tracks.'))
558
data_cd = optparse.OptionGroup(parser, _('Data CD Mode (-D) options'),
559
_('Data CD: adds files and directories.'))
562
action="callback", callback=varargs, dest="path",
564
'add file/s or path\'s content to CD-ROM/DVD\'s root.'
565
' e.g.: -p /cvs/myproj/ <return>. In this example we will find'
566
' CD-ROM/DVD\'s root filled with /cvs/myproj/ contents, but'
567
' no /cvs/myproj/ will be created.'))
569
"-r", "--preserve-path",
570
action="callback", callback=varargs, dest="preserve_path",
572
'add file/s or path\'s content to CD-ROM/DVD preserving'
573
' original path. e.g.: -r /cvs/myproj/ <return>. In this'
574
' example we will find /cvs/myproj/ in CD-ROM/DVD\'s root.'))
576
"-x", "--exclude-path",
577
#action="append", type="string", dest="exclude_path",
578
action="callback", callback=varargs, dest="exclude_path",
580
'every file or directory matching this string'
581
' will not be included.'))
583
"-c", "--change-path", action="append",
584
nargs=2, type="string", dest="change_path",
586
'usage: -c <new_path> <old_path>. With this option,'
587
' old_path will be named new_path in CD-ROM/DVD.'
588
' e.g.: -c /my_home/2004_Jan/ /home/bigpaul/ <return>.'
589
' Thus /home/bigpaul/ will be named /my_home/2004_Jan/'
592
"-l", "--follow-symlink", action="store_true", dest="follow_symlink",
593
help=_('this option allows burn to follow symbolic link directories'))
595
"-m", "--multisession", action="store_true", dest="multisession",
596
help=_('this option allows multisession CDs'))
599
iso_cd = optparse.OptionGroup(parser, "ISO CD Mode (-I) options",
600
_('Creates a CD-ROM/DVD from an existing image.'))
603
"-n", "--name", action="store", type="string", dest="iso_name",
604
help=_('image name'))
607
copy_cd = optparse.OptionGroup(
608
parser, _('Copy CD Mode (-C) options'),
610
'If you have both a reader and a recording unit'
611
' you can perform a copy on-the-fly. You can also copy a CD'
612
' even if you only have the recording unit.'))
615
audio_cd = optparse.OptionGroup(
616
parser, _('Audio CD Mode (-A) options'),
618
'Audio CD is used to create an audio CD-ROM'
619
' from .wav, .ogg and .mp3 files. You can can use -a option'
620
' to perform an Audio CD from different source audio files.'))
622
"-a", "--audio-file",
623
action="callback", callback=varargs, dest="general_audio_file_list",
625
'.wav, .mp3, .ogg file/s. Files must have extensions'
626
' (no matter if they are upper or lowercase).'))
629
action="store", type="string", dest="file_list",
630
help=_('m3u playlist or file with one audio file per line.'))
633
action="store_true", dest="clear_audio_temp",
634
help=_('remove temporary audio files.'))
636
"--no-gaps", action="store_true", dest="nogaps",
638
'Enable disk at once. Enabling this option, you will not have'
639
' 2 seconds gap between tracks.'))
641
parser.add_option_group(mode_options)
642
parser.add_option_group(general_option)
643
parser.add_option_group(data_cd)
644
parser.add_option_group(iso_cd)
645
parser.add_option_group(copy_cd)
646
parser.add_option_group(audio_cd)
647
(options, args) = parser.parse_args()
649
if len(sys.argv) > 1:
650
check_main_option(sys.argv[1])
656
configure.read_from_files()
658
prog_intro(options.mode)
659
cdrom = CDROM(options)
661
if options.mode is ContentMode.data:
662
print _('Checking files, directories and disk usage. Please wait...')
669
first_time_multisession = 0
672
for directory in options.path:
673
if os.path.exists(directory):
674
abspath = os.path.abspath(directory)
675
path = path + '\'' + abspath + '\'' + ' '
676
if os.path.isfile(abspath):
677
size = size + os.path.getsize(abspath)
678
elif os.path.isdir(abspath):
679
size = size + du(abspath, 0, options.follow_symlink)
681
err(abspath + _(': no such file or directory.'))
684
if options.preserve_path:
685
for directory in options.preserve_path:
686
if os.path.exists(directory):
687
abspath = os.path.abspath(directory)
688
if os.path.isdir(abspath):
689
size = size + du(abspath, 0, options.follow_symlink)
690
elif os.path.isfile(abspath):
691
size = size + os.path.getsize(abspath)
694
err(abspath + _(': no such file or directory.'))
696
path_preserved = path_preserved \
697
+ '\'' + abspath + '\'' + '=' \
698
+ '\'' + abspath + '\'' + ' '
701
if options.change_path:
702
for (new_path, old_path) in options.change_path:
703
if os.path.exists(old_path):
704
abspath = os.path.abspath(old_path)
705
if os.path.isfile(abspath):
706
size += os.path.getsize(abspath)
707
elif os.path.isdir(abspath):
708
size += du(abspath, 0, options.follow_symlink)
710
err(abspath + _(': no such file or directory.'))
712
path_changed = path_changed \
713
+ '\'' + new_path + '\'' + '=' \
714
+ '\'' + abspath + '\'' + ' '
718
'nothing will be done for %(old_path)s -> %(new_path)s'
720
if options.exclude_path:
722
for directory in options.path:
723
if os.path.isdir(directory):
724
for exclude_path in options.exclude_path:
725
for root, dirs, files in os.walk(directory):
726
for file_name in files:
727
if fnmatch.fnmatch(file_name, exclude_path):
729
os.path.join(root, file_name)):
731
os.path.join(root, file_name)):
732
size = size - os.path.getsize(
733
os.path.join(root, file_name))
734
paths_excluded.append(
735
os.path.join(root, file_name))
737
if fnmatch.fnmatch(subdir, exclude_path):
739
os.path.join(root, subdir)):
741
os.path.join(root, subdir), 0,
742
options.follow_symlink)
743
paths_excluded.append(
744
os.path.join(root, subdir))
746
print _('Size without exclusions: '), "\t", testsize/1048576, "Mb"
748
global_path = path + path_preserved + path_changed
749
if global_path == '':
750
err(_('Nothing to be burned...'))
752
cdrom.size_compare(size, size)
754
cdrom.iso.genisoimage_args.extend(
755
[config.get('executables', 'genisoimage'), "-R"])
756
if os.path.exists(cdrom.iso.tempdir):
757
cdrom.iso.genisoimage_args.extend(["-o", cdrom.iso.dest])
759
err(_('Error: ') + cdrom.iso.tempdir + _(' does not exist'))
762
if cdrom.iso.windows:
763
cdrom.iso.genisoimage_args.extend(["-J", "-joliet-long"])
765
for path_excluded in paths_excluded:
766
cdrom.iso.genisoimage_args.extend(["-x", path_excluded])
768
if options.multisession:
769
multisession_choose = cdrom.iso.ask_multisession()
770
if multisession_choose == 2:
773
'Place target CD in CD/DVD writer unit and press a key...')
775
print _('Please wait...')
777
config.get('executables', 'wodim'), "-msinfo",
778
"dev=%(device)s" % vars(cdrom)]
779
command_process = subprocess.Popen(
781
stdout=subprocess.PIPE, stderr=open(os.devnull))
782
msinfo = command_process.communicate()[0]
783
cdrom.iso.genisoimage_args.extend(
784
["-C", msinfo, "-M", cdrom.device])
785
elif not multisession_choose == 1:
788
first_time_multisession = 1
789
cdrom.iso.genisoimage_args.extend(["-graft-points", global_path])
790
cdrom.make_command_args()
791
if os.path.exists(cdrom.iso.dest):
792
if not cdrom.iso.first_ask_remove():
796
if first_time_multisession == 1:
797
cdrom.iso.ask_mount(cdrom.iso.dest)
799
if os.path.exists(cdrom.iso.dest):
800
if not cdrom.iso.ask_remove():
804
if options.mode is ContentMode.iso:
805
if os.path.exists(options.iso_name):
806
if cdrom.size_compare(os.path.getsize(options.iso_name), 0):
808
cdrom.iso.dest = options.iso_name
809
cdrom.iso.ask_mount(options.iso_name)
810
cdrom.make_command_args()
812
if os.path.exists(cdrom.iso.dest):
813
if not cdrom.iso.ask_remove():
818
err_nf(options.iso_name)
822
if options.mode is ContentMode.copy:
823
cdrom.compute_media_size()
824
single_drive_mode = 0
825
if cdrom.device == cdrom.source_device or cdrom.source_device == '':
826
single_drive_mode = 1
827
#print single_drive_mode
828
cdrdao_args = [config.get('executables', 'cdrdao')]
830
if single_drive_mode == 1:
831
cdrdao_args.append("copy")
833
cdrdao_args.append("--simulate")
835
cdrdao_args.append("--eject")
836
cdrdao_args.extend(["--datafile", cdrom.iso.dest])
837
cdrdao_args.extend(["--device", cdrom.device])
838
if not cdrom.driver == '':
839
cdrdao_args.extend(["--driver", cdrom.driver])
840
cdrdao_args.extend(["--speed", str(cdrom.speed)])
841
cdrdao_args.append("--fast-toc")
842
cdrom.single_dao_create(cdrdao_args)
844
cdrdao_args.append("copy")
846
cdrdao_args.append("--simulate")
848
cdrdao_args.append("--eject")
849
cdrdao_args.extend(["--device", cdrom.device])
850
if not cdrom.driver == '':
851
cdrdao_args.extend(["--driver", cdrom.driver])
852
cdrdao_args.extend(["--source-device", cdrom.source_device])
853
if not cdrom.source_driver == '':
854
cdrdao_args.extend(["--source-driver", cdrom.source_driver])
855
cdrdao_args.extend(["--speed", str(cdrom.speed)])
856
cdrdao_args.append("--on-the-fly")
857
cdrdao_args.append("--fast-toc")
858
cdrom.double_dao_create(cdrdao_args)
861
if options.mode is ContentMode.audio:
862
print _('Audio file processing. Please wait...')
876
#61196 wav header????
877
#176400 kb 1 wav second comes from:
878
#44100 * 16 * 2bit / 8 = byte
880
list_dir = os.listdir(cdrom.iso.tempdir)
882
if options.clear_audio_temp:
883
for file_path in list_dir:
884
if re.compile("^burn_1").search(file_path[:5], 0):
885
old_temp_wavs.append(
886
os.path.normpath(cdrom.iso.tempdir + file_path))
889
for old_wavs in old_temp_wavs:
890
print _('removing '), old_wavs, "..."
892
if options.general_audio_file_list:
893
file_list = options.general_audio_file_list
895
if options.file_list:
896
file_list.extend(get_list_from_file(options.file_list))
898
for file_path in file_list:
899
base, ext = os.path.splitext(file_path)
901
if ext == '.ogg' or ext == '.mp3':
903
audio.file_duration(file_path) * 176400)
905
size += os.path.getsize(file_path)
906
if ext != '.ogg' and ext != '.mp3' and ext != '.wav':
907
print file_path, _(': not a regular audio file. Skipped')
909
cdrom.size_compare((size+mp3_ogg_size), mp3_ogg_size)
912
list_dir = os.listdir(cdrom.iso.tempdir)
914
for file_path in list_dir:
915
if re.compile("^burn_1").search(file_path[:5], 0):
916
old_temp_wavs.append(
917
os.path.normpath(cdrom.iso.tempdir + file_path))
920
for wav_path in old_temp_wavs:
922
if console.ask_yesno(_(
923
'You have old burn audio files in temporary directory.'
924
' Remove these files and continue'
926
for oldwavs in old_temp_wavs:
927
print _('removing '), oldwavs, "..."
932
print "---------------------------------------------"
933
print "Burn - " + _('Track summary')
934
print "---------------------------------------------"
935
for file_path in file_list:
936
track_number = track_number + 1
937
base, ext = os.path.splitext(file_path)
940
if ext == '.mp3' or ext == '.ogg':
941
if os.path.exists(file_path):
942
abspath = os.path.abspath(file_path)
943
if os.path.isfile(abspath):
944
info = audio.FileInfo(abspath)
946
print track_number, ")\t", \
947
info.duration, "-", info.title
949
print track_number, ")\t", \
950
info.duration, "-", os.path.abspath(file_path)
951
total_audio_time += info.total_time
953
print track_number, ")\t", audio.compute_duration(
954
os.path.getsize(os.path.abspath(file_path)) / 176400), \
955
"-", os.path.abspath(file_path)
956
total_audio_time += os.path.getsize(
957
os.path.abspath(file_path)) / 176400
958
# print "Total audio time: ", int(total_audio_time)
960
print _('Total Audio-CD: '), audio.compute_duration(
961
int(total_audio_time))
963
if config.getboolean('general', 'external_decoding'):
964
print _('Performing audio decoding with external decoder.')
965
ogg_decoder = config.get('executables', 'ogg_decoder')
966
mp3_decoder = config.get('executables', 'mp3_decoder')
967
ogg_decoder_option = config.get(
968
'executables', 'ogg_decoder_option')
969
mp3_decoder_option = config.get(
970
'executables', 'mp3_decoder_option')
972
print _('Performing audio decoding with burn\'s native functions.')
973
for file_path in file_list:
974
track_number2 = track_number2 + 1
975
base, ext = os.path.splitext(file_path)
978
if ext == '.mp3' or ext == '.ogg':
979
counter = counter + 1
984
if os.path.exists(file_path):
985
abspath = os.path.abspath(file_path)
986
if os.path.isfile(abspath):
987
#Shows full path ogg files
989
"[%(track_number2)d/%(track_number)d]"
990
" %(track_type)s\tProcessing %(abspath)s") % vars()
991
info = audio.FileInfo(abspath)
993
#print "\t\tTitle: \t\t",info.title
994
#print "\t\tAlbum: \t\t",info.album
995
#print "\t\tArtist: \t",info.artist
996
#Convert mp3/ogg file in tempdir/file.[mp3|ogg].wav
998
if config.getboolean('general', 'external_decoding'):
999
wav_name = "burn_%(counter)d.wav" % vars()
1000
wav_path = os.path.normpath(os.path.join(
1001
cdrom.iso.tempdir, wav_name))
1002
command_args = [mp3_decoder]
1003
command_args.extend(
1004
shlex.split(mp3_decoder_option))
1005
command_args.extend([wav_path, abspath])
1006
subprocess.call(command_args)
1008
dev = ao.AudioDevice(
1012
+ 'burn_' + repr(counter))
1015
pbar = console.ProgressBar(width=30)
1016
audio_buffer = mad.MadFile(abspath)
1019
buf = audio_buffer.read()
1023
audio_buffer.current_time() * 100 / \
1024
audio_buffer.total_time()
1025
if progress > old_progress:
1026
pbar.amount = progress
1027
pbar.update_display()
1028
old_progress = progress
1029
dev.play(buf, len(buf))
1030
audio_list.append(os.path.normpath(
1032
+ 'burn_' + repr(counter)) + '.wav')
1033
to_be_removed.append(os.path.normpath(
1035
+ 'burn_' + repr(counter)) + '.wav')
1038
if config.getboolean('general', 'external_decoding'):
1039
wav_name = "burn_%(counter)d.wav" % vars()
1040
wav_path = os.path.normpath(os.path.join(
1041
cdrom.iso.tempdir, wav_name))
1042
command_args = [ogg_decoder]
1043
command_args.extend(
1044
shlex.split(ogg_decoder_option))
1045
command_args.extend([wav_path, abspath])
1046
subprocess.call(command_args)
1048
dev = ao.AudioDevice(
1049
'wav', filename=os.path.normpath(
1051
+ 'burn_' + repr(counter))
1054
pbar = console.ProgressBar(width=30)
1055
audiofile = ogg.vorbis.VorbisFile(abspath)
1058
(buf, bytes, bit) = audiofile.read(size)
1062
audiofile.time_tell() * 100 / \
1063
audiofile.time_total(-1)
1064
if progress > old_progress:
1065
pbar.amount = progress
1066
pbar.update_display()
1067
old_progress = progress
1068
dev.play(buf, bytes)
1070
audio_list.append(os.path.normpath(
1072
+ 'burn_' + repr(counter)) +'.wav')
1073
to_be_removed.append(os.path.normpath(
1075
+ 'burn_' + repr(counter)) +'.wav')
1077
err(abspath + _(': not a valid audio file.'))
1081
if os.path.exists(file_path):
1082
abspath = os.path.abspath(file_path)
1084
"[%(track_number2)d/%(track_number)d]"
1085
" %(track_type)s\tProcessing %(abspath)s") % vars()
1086
audio_list.append(abspath)
1088
cdrom.make_command_args()
1090
cdrom.wodim_args.append("-dao")
1091
cdrom.wodim_args.append("-audio")
1092
cdrom.wodim_args.extend(audio_list)
1096
if config.getboolean('general', 'pause'):
1097
while console.ask_yesno(_(
1098
'Do you want to use processed audio files to create'
1099
' another Audio CD now'), False):
1102
while console.ask_yesno(_(
1103
'Write another copy (insert a blank disc now)'),
1108
#removing temp audio files
1109
for file_path in to_be_removed:
1110
if os.path.exists(file_path):
1111
print _('removing '), file_path, "..."
1112
os.remove(file_path)
1117
if __name__ == '__main__':
1120
except KeyboardInterrupt:
1122
print _('burn: exiting now...')
1129
# vim: filetype=python fileencoding=utf-8 :