ripcd: Support ripping to Flac as well as Vorbis
parent
27d13532ea
commit
8801b6433c
64
ripcd.py
64
ripcd.py
|
@ -100,7 +100,7 @@ class Track(object):
|
||||||
'Tracktitle': 'title',
|
'Tracktitle': 'title',
|
||||||
}
|
}
|
||||||
|
|
||||||
FILENAME_FORMAT = '{tracknumber:0>2} {artist} - {title}.ogg'
|
FILENAME_FORMAT = '{tracknumber:0>2} {artist} - {title}.{ext}'
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.filename = None
|
self.filename = None
|
||||||
|
@ -108,10 +108,13 @@ class Track(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def outfile(self):
|
def outfile(self):
|
||||||
if self.tags:
|
if self.tags.get('title'):
|
||||||
return self.FILENAME_FORMAT.format(**self.tags)
|
return self.FILENAME_FORMAT.format(ext=self.extension, **self.tags)
|
||||||
else:
|
else:
|
||||||
return os.path.splitext(self.filename)[0] + '.ogg'
|
return '{filename}.{ext}'.format(
|
||||||
|
filename=os.path.splitext(self.filename)[0],
|
||||||
|
ext=self.extension,
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_file(cls, filename):
|
def from_file(cls, filename):
|
||||||
|
@ -145,13 +148,28 @@ class Track(object):
|
||||||
self.tags[tag] = titlecase(value.strip().strip("'"))
|
self.tags[tag] = titlecase(value.strip().strip("'"))
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def to_vorbis(self, lock=None):
|
def encode(self, lock=None):
|
||||||
assert self.filename
|
assert self.filename
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
yield from loop.run_in_executor(None, self._parse_inf)
|
yield from loop.run_in_executor(None, self._parse_inf)
|
||||||
if lock:
|
if lock:
|
||||||
yield from lock.acquire()
|
yield from lock.acquire()
|
||||||
print('Encoding {} as {}'.format(self.filename, self.outfile))
|
print('Encoding {} as {}'.format(self.filename, self.outfile))
|
||||||
|
|
||||||
|
def write_tags(self):
|
||||||
|
tags = mutagen.File(self.outfile, easy=True)
|
||||||
|
tags.update(self.tags)
|
||||||
|
tags.save()
|
||||||
|
|
||||||
|
|
||||||
|
class VorbisTrack(Track):
|
||||||
|
|
||||||
|
extension = 'ogg'
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def encode(self, lock=None):
|
||||||
|
yield from super(VorbisTrack, self).encode(lock)
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
cmd = ['oggenc', '-q', '9', '-Q', '-o', self.outfile, self.filename]
|
cmd = ['oggenc', '-q', '9', '-Q', '-o', self.outfile, self.filename]
|
||||||
p = yield from asyncio.create_subprocess_exec(*cmd)
|
p = yield from asyncio.create_subprocess_exec(*cmd)
|
||||||
yield from p.wait()
|
yield from p.wait()
|
||||||
|
@ -162,10 +180,24 @@ class Track(object):
|
||||||
if self.tags:
|
if self.tags:
|
||||||
yield from loop.run_in_executor(None, self.write_tags)
|
yield from loop.run_in_executor(None, self.write_tags)
|
||||||
|
|
||||||
def write_tags(self):
|
|
||||||
tags = mutagen.File(self.outfile, easy=True)
|
class FlacTrack(Track):
|
||||||
tags.update(self.tags)
|
|
||||||
tags.save()
|
extension = 'flac'
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def encode(self, lock=None):
|
||||||
|
yield from super(FlacTrack, self).encode(lock)
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
cmd = ['flac', '-s', '-o', self.outfile, self.filename]
|
||||||
|
p = yield from asyncio.create_subprocess_exec(*cmd)
|
||||||
|
yield from p.wait()
|
||||||
|
if p.returncode != 0:
|
||||||
|
sys.stderr.write('Failed to encode {}\n'.format(self.filename))
|
||||||
|
if lock:
|
||||||
|
lock.release()
|
||||||
|
if self.tags:
|
||||||
|
yield from loop.run_in_executor(None, self.write_tags)
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
@ -222,13 +254,14 @@ def rip_info(device):
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def rip_tracks(device, num_encoders=None):
|
def rip_tracks(device, codec, num_encoders=None):
|
||||||
if not num_encoders:
|
if not num_encoders:
|
||||||
num_encoders = multiprocessing.cpu_count()
|
num_encoders = multiprocessing.cpu_count()
|
||||||
cmd = ['icedax']
|
cmd = ['icedax']
|
||||||
if device:
|
if device:
|
||||||
cmd.extend(('--device', device))
|
cmd.extend(('--device', device))
|
||||||
cmd.extend((
|
cmd.extend((
|
||||||
|
'--max',
|
||||||
'--alltracks',
|
'--alltracks',
|
||||||
'--no-infofile',
|
'--no-infofile',
|
||||||
'--verbose-level', 'summary',
|
'--verbose-level', 'summary',
|
||||||
|
@ -242,7 +275,13 @@ def rip_tracks(device, num_encoders=None):
|
||||||
lock = asyncio.Semaphore(num_encoders)
|
lock = asyncio.Semaphore(num_encoders)
|
||||||
tasks = []
|
tasks = []
|
||||||
for filename in glob.glob('*.wav'):
|
for filename in glob.glob('*.wav'):
|
||||||
tasks.append(Track.from_file(filename).to_vorbis(lock))
|
if codec == 'vorbis':
|
||||||
|
track = VorbisTrack.from_file(filename)
|
||||||
|
elif codec == 'flac':
|
||||||
|
track = FlacTrack.from_file(filename)
|
||||||
|
else:
|
||||||
|
raise ValueError('Unsupported codec: {}'.format(codec))
|
||||||
|
tasks.append(track.encode(lock))
|
||||||
yield from asyncio.wait(tasks)
|
yield from asyncio.wait(tasks)
|
||||||
|
|
||||||
|
|
||||||
|
@ -282,6 +321,9 @@ def parse_args():
|
||||||
parser.add_argument('--no-clean', dest='cleanup', action='store_false',
|
parser.add_argument('--no-clean', dest='cleanup', action='store_false',
|
||||||
default=True,
|
default=True,
|
||||||
help='Do not remove temporary files')
|
help='Do not remove temporary files')
|
||||||
|
parser.add_argument('--codec', '-c', choices=('vorbis', 'flac'),
|
||||||
|
default='vorbis',
|
||||||
|
help='Encode audio with specific codec')
|
||||||
parser.add_argument('device', nargs='?',
|
parser.add_argument('device', nargs='?',
|
||||||
help='CD-ROM device to use')
|
help='CD-ROM device to use')
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
Loading…
Reference in New Issue