diff --git a/headphones/__init__.py b/headphones/__init__.py index 8a4637bc..5fec6b34 100644 --- a/headphones/__init__.py +++ b/headphones/__init__.py @@ -218,6 +218,8 @@ ENCODEROUTPUTFORMAT = None ENCODERQUALITY = None ENCODERVBRCBR = None ENCODERLOSSLESS = False +ENCODER_MULTICORE = False +ENCODER_MULTICORE_COUNT = 0 DELETE_LOSSLESS_FILES = False PROWL_ENABLED = True PROWL_PRIORITY = 1 @@ -337,7 +339,7 @@ def initialize(): NZBSORG, NZBSORG_UID, NZBSORG_HASH, NZBSRUS, NZBSRUS_UID, NZBSRUS_APIKEY, OMGWTFNZBS, OMGWTFNZBS_UID, OMGWTFNZBS_APIKEY, \ NZB_DOWNLOADER, TORRENT_DOWNLOADER, PREFERRED_WORDS, REQUIRED_WORDS, IGNORED_WORDS, LASTFM_USERNAME, \ INTERFACE, FOLDER_PERMISSIONS, FILE_PERMISSIONS, ENCODERFOLDER, ENCODER_PATH, ENCODER, XLDPROFILE, BITRATE, SAMPLINGFREQUENCY, \ - MUSIC_ENCODER, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, ENCODERQUALITY, ENCODERVBRCBR, ENCODERLOSSLESS, DELETE_LOSSLESS_FILES, \ + MUSIC_ENCODER, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, ENCODERQUALITY, ENCODERVBRCBR, ENCODERLOSSLESS, ENCODER_MULTICORE, ENCODER_MULTICORE_COUNT, DELETE_LOSSLESS_FILES, \ PROWL_ENABLED, PROWL_PRIORITY, PROWL_KEYS, PROWL_ONSNATCH, PUSHOVER_ENABLED, PUSHOVER_PRIORITY, PUSHOVER_KEYS, PUSHOVER_ONSNATCH, MIRRORLIST, \ TWITTER_ENABLED, TWITTER_ONSNATCH, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, \ PUSHBULLET_ENABLED, PUSHBULLET_APIKEY, PUSHBULLET_DEVICEID, PUSHBULLET_ONSNATCH, \ @@ -534,6 +536,8 @@ def initialize(): ENCODERQUALITY = check_setting_int(CFG, 'General', 'encoderquality', 2) ENCODERVBRCBR = check_setting_str(CFG, 'General', 'encodervbrcbr', 'cbr') ENCODERLOSSLESS = bool(check_setting_int(CFG, 'General', 'encoderlossless', 1)) + ENCODER_MULTICORE = bool(check_setting_int(CFG, 'General', 'encoder_multicore', 0)) + ENCODER_MULTICORE_COUNT = max(0, check_setting_int(CFG, 'General', 'encoder_multicore_count', 0)) DELETE_LOSSLESS_FILES = bool(check_setting_int(CFG, 'General', 'delete_lossless_files', 1)) PROWL_ENABLED = bool(check_setting_int(CFG, 'Prowl', 'prowl_enabled', 0)) @@ -1006,6 +1010,8 @@ def config_write(): new_config['General']['encoderquality'] = ENCODERQUALITY new_config['General']['encodervbrcbr'] = ENCODERVBRCBR new_config['General']['encoderlossless'] = int(ENCODERLOSSLESS) + new_config['General']['encoder_multicore'] = int(ENCODER_MULTICORE) + new_config['General']['encoder_multicore_count'] = int(ENCODER_MULTICORE_COUNT) new_config['General']['delete_lossless_files'] = int(DELETE_LOSSLESS_FILES) new_config['General']['mirror'] = MIRROR diff --git a/headphones/music_encoder.py b/headphones/music_encoder.py index e37f2d43..a46d5f90 100644 --- a/headphones/music_encoder.py +++ b/headphones/music_encoder.py @@ -17,6 +17,7 @@ import os import headphones import shutil import time +import multiprocessing import subprocess from headphones import logger @@ -100,6 +101,7 @@ def encode(albumPath): i=0 encoder_failed = False + jobs = [] for music in musicFiles: infoMusic=MediaFile(music) @@ -131,15 +133,45 @@ def encode(albumPath): encode = True # encode if encode: - if not command(encoder,music,musicTempFiles[i],albumPath): - encoder_failed = True - break + job = (encoder, music, musicTempFiles[i], albumPath) + jobs.append(job) else: musicFiles[i] = None musicTempFiles[i] = None i=i+1 + # Encode music files + if len(jobs) > 0: + if headphones.ENCODER_MULTICORE: + if headphones.ENCODER_MULTICORE_COUNT == 0: + processes = multiprocessing.cpu_count() + else: + processes = headphones.ENCODER_MULTICORE_COUNT + + logger.debug("Multi-core encoding enabled, %d processes" % processes) + else: + processes = 1 + + # Use multiprocessing only if it's worth the overhead. and if it is + # enabled. If not, then use the old fashioned way. + if processes > 1: + pool = multiprocessing.Pool(processes=processes) + results = pool.map_async(command_map, jobs) + + # No new processes will be created, so close it and wait for all + # processes to finish + pool.close() + pool.join() + + # Retrieve the results + results = results.get() + else: + results = map(command_map, jobs) + + # The results are either True or False, so determine if one is False + encoder_failed = not all(results) + musicFiles = filter(None, musicFiles) musicTempFiles = filter(None, musicTempFiles) @@ -187,7 +219,10 @@ def encode(albumPath): logger.info('Encoding for folder %s is not required' % (albumPath.decode(headphones.SYS_ENCODING, 'replace'))) return musicFinalFiles - + +def command_map(args): + return command(*args) + def command(encoder,musicSource,musicDest,albumPath): cmd=[]