[ExtractAudio] Support alac

Closes #1707
This commit is contained in:
pukkandan 2021-11-19 05:20:13 +05:30
parent 8863c8f09e
commit 467b6b8387
No known key found for this signature in database
GPG key ID: 0F00D95A001F4698

View file

@ -53,6 +53,7 @@ ACODECS = {
'opus': 'libopus', 'opus': 'libopus',
'vorbis': 'libvorbis', 'vorbis': 'libvorbis',
'wav': None, 'wav': None,
'alac': None,
} }
@ -383,7 +384,7 @@ class FFmpegPostProcessor(PostProcessor):
class FFmpegExtractAudioPP(FFmpegPostProcessor): class FFmpegExtractAudioPP(FFmpegPostProcessor):
COMMON_AUDIO_EXTS = ('wav', 'flac', 'm4a', 'aiff', 'mp3', 'ogg', 'mka', 'opus', 'wma') COMMON_AUDIO_EXTS = ('wav', 'flac', 'm4a', 'aiff', 'mp3', 'ogg', 'mka', 'opus', 'wma')
SUPPORTED_EXTS = ('best', 'aac', 'flac', 'mp3', 'm4a', 'opus', 'vorbis', 'wav') SUPPORTED_EXTS = ('best', 'aac', 'flac', 'mp3', 'm4a', 'opus', 'vorbis', 'wav', 'alac')
def __init__(self, downloader=None, preferredcodec=None, preferredquality=None, nopostoverwrites=False): def __init__(self, downloader=None, preferredcodec=None, preferredquality=None, nopostoverwrites=False):
FFmpegPostProcessor.__init__(self, downloader) FFmpegPostProcessor.__init__(self, downloader)
@ -399,10 +400,10 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):
limits = { limits = {
'libmp3lame': (10, 0), 'libmp3lame': (10, 0),
'libvorbis': (0, 10),
# FFmpeg's AAC encoder does not have an upper limit for the value of -q:a. # FFmpeg's AAC encoder does not have an upper limit for the value of -q:a.
# Experimentally, with values over 4, bitrate changes were minimal or non-existent # Experimentally, with values over 4, bitrate changes were minimal or non-existent
'aac': (0.1, 4), 'aac': (0.1, 4),
'vorbis': (0, 10),
'libfdk_aac': (1, 5), 'libfdk_aac': (1, 5),
}.get(codec) }.get(codec)
if not limits: if not limits:
@ -426,7 +427,7 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):
@PostProcessor._restrict_to(images=False) @PostProcessor._restrict_to(images=False)
def run(self, information): def run(self, information):
path = information['filepath'] orig_path = path = information['filepath']
orig_ext = information['ext'] orig_ext = information['ext']
if self._preferredcodec == 'best' and orig_ext in self.COMMON_AUDIO_EXTS: if self._preferredcodec == 'best' and orig_ext in self.COMMON_AUDIO_EXTS:
@ -452,6 +453,10 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):
more_opts = ['-f', 'adts'] more_opts = ['-f', 'adts']
if filecodec == 'vorbis': if filecodec == 'vorbis':
extension = 'ogg' extension = 'ogg'
elif filecodec == 'alac':
acodec = None
extension = 'm4a'
more_opts += ['-acodec', 'alac']
else: else:
# MP3 otherwise. # MP3 otherwise.
acodec = 'libmp3lame' acodec = 'libmp3lame'
@ -466,42 +471,49 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):
more_opts = self._quality_args(acodec) more_opts = self._quality_args(acodec)
if self._preferredcodec == 'aac': if self._preferredcodec == 'aac':
more_opts += ['-f', 'adts'] more_opts += ['-f', 'adts']
if self._preferredcodec == 'm4a': elif self._preferredcodec == 'm4a':
more_opts += ['-bsf:a', 'aac_adtstoasc'] more_opts += ['-bsf:a', 'aac_adtstoasc']
if self._preferredcodec == 'vorbis': elif self._preferredcodec == 'vorbis':
extension = 'ogg' extension = 'ogg'
if self._preferredcodec == 'wav': elif self._preferredcodec == 'wav':
extension = 'wav' extension = 'wav'
more_opts += ['-f', 'wav'] more_opts += ['-f', 'wav']
elif self._preferredcodec == 'alac':
extension = 'm4a'
more_opts += ['-acodec', 'alac']
prefix, sep, ext = path.rpartition('.') # not os.path.splitext, since the latter does not work on unicode in all setups prefix, sep, ext = path.rpartition('.') # not os.path.splitext, since the latter does not work on unicode in all setups
new_path = prefix + sep + extension temp_path = new_path = prefix + sep + extension
information['filepath'] = new_path if new_path == path:
information['ext'] = extension orig_path = prepend_extension(path, 'orig')
temp_path = prepend_extension(path, 'temp')
# If we download foo.mp3 and convert it to... foo.mp3, then don't delete foo.mp3, silly. if (self._nopostoverwrites and os.path.exists(encodeFilename(new_path))
if (new_path == path and os.path.exists(encodeFilename(orig_path))):
or (self._nopostoverwrites and os.path.exists(encodeFilename(new_path)))):
self.to_screen('Post-process file %s exists, skipping' % new_path) self.to_screen('Post-process file %s exists, skipping' % new_path)
return [], information return [], information
try: try:
self.to_screen('Destination: ' + new_path) self.to_screen(f'Destination: {new_path}')
self.run_ffmpeg(path, new_path, acodec, more_opts) self.run_ffmpeg(path, temp_path, acodec, more_opts)
except AudioConversionError as e: except AudioConversionError as e:
raise PostProcessingError( raise PostProcessingError(
'audio conversion failed: ' + e.msg) 'audio conversion failed: ' + e.msg)
except Exception: except Exception:
raise PostProcessingError('error running ' + self.basename) raise PostProcessingError('error running ' + self.basename)
os.replace(path, orig_path)
os.replace(temp_path, new_path)
information['filepath'] = new_path
information['ext'] = extension
# Try to update the date time for extracted audio file. # Try to update the date time for extracted audio file.
if information.get('filetime') is not None: if information.get('filetime') is not None:
self.try_utime( self.try_utime(
new_path, time.time(), information['filetime'], new_path, time.time(), information['filetime'],
errnote='Cannot update utime of audio file') errnote='Cannot update utime of audio file')
return [path], information return [orig_path], information
class FFmpegVideoConvertorPP(FFmpegPostProcessor): class FFmpegVideoConvertorPP(FFmpegPostProcessor):