From fda2b28162ff659deb5dc87c323f2c68a2adf65b Mon Sep 17 00:00:00 2001 From: Phiol Borman Date: Wed, 26 Feb 2020 11:11:04 +0100 Subject: [PATCH] Moved preprocessor into main program --- data/interfaces/bookstrap/config.html | 125 ++++++- data/interfaces/bookstrap/config_functions.js | 2 +- ...le_preprocessor.py => demo_preprocessor.py | 5 + lazylibrarian/__init__.py | 15 +- lazylibrarian/dbupgrade.py | 13 +- lazylibrarian/postprocess.py | 23 +- lazylibrarian/preprocessor.py | 348 ++++++++++++++++++ lazylibrarian/webServe.py | 6 +- 8 files changed, 516 insertions(+), 21 deletions(-) rename example_preprocessor.py => demo_preprocessor.py (98%) mode change 100755 => 100644 create mode 100644 lazylibrarian/preprocessor.py diff --git a/data/interfaces/bookstrap/config.html b/data/interfaces/bookstrap/config.html index 38f14160..94a5b022 100644 --- a/data/interfaces/bookstrap/config.html +++ b/data/interfaces/bookstrap/config.html @@ -2280,13 +2280,31 @@ This will include details for other books by new authors found in wishlists or csv files,
or when manually importing a book by a new author
+
+ External Programs
- - - Path to preprocessor to run before importing books into the library + + + Path to additional preprocessor to run before importing books into the library
- Calibre +
+ + + Path to "ebook-convert" to convert book formats +
+
+ + + Path to "ffmpeg" to merge audiobook parts or add tags +
+
+ + + Git program unless already in your path. Usually leave this blank +
+
+ Calibre
@@ -2645,11 +2663,6 @@
-
- - - Git program unless already in your path. Usually leave this blank -
%if lazylibrarian.CONFIG['MAG_TAB'] == True:
@@ -2679,6 +2692,8 @@ Create opf files for magazines
%endif +
+
%if lazylibrarian.CONFIG['COMIC_TAB'] == True:
<% @@ -2811,6 +2826,96 @@
%endif
+
+ eBook Conversions +
+ + +
+
+ <% + if lazylibrarian.CONFIG['KEEP_OPF'] == True: + checked = 'checked="checked"' + else: + checked = '' + %> + +
+
+ <% + if lazylibrarian.CONFIG['KEEP_JPG'] == True: + checked = 'checked="checked"' + else: + checked = '' + %> + +
+
+ <% + if lazylibrarian.CONFIG['DELETE_OTHER_FORMATS'] == True: + checked = 'checked="checked"' + else: + checked = '' + %> + +
+ AudioBook Conversions +
+ + +
+
+ <% + if lazylibrarian.CONFIG['CREATE_SINGLEAUDIO'] == True: + checked = 'checked="checked"' + else: + checked = '' + %> + +
+
+ <% + if lazylibrarian.CONFIG['KEEP_SEPARATEAUDIO'] == True: + checked = 'checked="checked"' + else: + checked = '' + %> + +
+
+ <% + if lazylibrarian.CONFIG['WRITE_AUDIOTAGS'] == True: + checked = 'checked="checked"' + else: + checked = '' + %> + +
+ Magazine Conversions +
+ <% + if lazylibrarian.CONFIG['SWAP_COVERPAGE'] == True: + checked = 'checked="checked"' + else: + checked = '' + %> + +
+

@@ -3919,7 +4024,7 @@ - + %endfor diff --git a/data/interfaces/bookstrap/config_functions.js b/data/interfaces/bookstrap/config_functions.js index 3d1039fb..35370849 100644 --- a/data/interfaces/bookstrap/config_functions.js +++ b/data/interfaces/bookstrap/config_functions.js @@ -1319,7 +1319,7 @@ }); $('#testpreprocessor').click(function () { - var prg = $.trim($("#imp_preprocessor").val()); + var prg = $.trim($("#ext_preprocessor").val()); $.get("testPreProcessor", { 'prg': prg}, function (data) { bootbox.dialog({ diff --git a/example_preprocessor.py b/demo_preprocessor.py old mode 100755 new mode 100644 similarity index 98% rename from example_preprocessor.py rename to demo_preprocessor.py index e6f3b884..aff7a147 --- a/example_preprocessor.py +++ b/demo_preprocessor.py @@ -11,6 +11,11 @@ # Anything you print to stdout appears as debug messages in the log # The exit code and messages get passed back to the "test" button # Always exit zero on success, non-zero on fail +# +################################################################ +# NOTE all of the features in the example preprocessor +# are already included in the main lazylibrarian program +################################################################ import os import subprocess diff --git a/lazylibrarian/__init__.py b/lazylibrarian/__init__.py index 9d1df3c9..9e0f87ce 100644 --- a/lazylibrarian/__init__.py +++ b/lazylibrarian/__init__.py @@ -210,7 +210,7 @@ CONFIG_NONDEFAULT = ['BOOKSTRAP_THEME', 'AUDIOBOOK_TYPE', 'AUDIO_DIR', 'AUDIO_TA 'AUTOADDMAG', 'AUTOADD_MAGONLY', 'TRANSMISSION_DIR', 'DELUGE_DIR', 'QBITTORRENT_DIR', 'BANNED_EXT', 'MAG_RENAME', 'LOGFILES', 'LOGSIZE', 'ISS_FORMAT', 'DATE_FORMAT', 'NO_ISBN', 'NO_SETS', 'NO_LANG', 'NO_PUBDATE', 'IMP_IGNORE', 'IMP_GOOGLEIMAGE', 'DELETE_CSV', - 'BLACKLIST_FAILED', 'BLACKLIST_PROCESSED', 'WISHLIST_INTERVAL', 'IMP_PREPROCESS', + 'BLACKLIST_FAILED', 'BLACKLIST_PROCESSED', 'WISHLIST_INTERVAL', 'EXT_PREPROCESS', 'OPDS_ENABLED', 'OPDS_AUTHENTICATION', 'OPDS_USERNAME', 'OPDS_PASSWORD', 'OPDS_METAINFO', 'OPDS_PAGE', 'DELAYSEARCH', 'SEED_WAIT', 'GR_AOWNED', 'GR_AWANTED', 'MAG_DELFOLDER', 'ADMIN_EMAIL', 'RSS_ENABLED', 'RSS_HOST', 'RSS_PODCAST', 'COMIC_TAB', 'COMIC_DEST_FOLDER', @@ -310,7 +310,7 @@ CONFIG_DEFINITIONS = { 'IMP_MAGCOVER': ('bool', 'General', 1), 'IMP_COMICCOVER': ('bool', 'General', 1), 'IMP_CONVERT': ('str', 'General', ''), - 'IMP_PREPROCESS': ('str', 'General', ''), + 'EXT_PREPROCESS': ('str', 'General', ''), 'GIT_PROGRAM': ('str', 'General', ''), 'CACHE_AGE': ('int', 'General', 30), 'TASK_AGE': ('int', 'General', 2), @@ -628,6 +628,17 @@ CONFIG_DEFINITIONS = { 'PREF_UNRARLIB': ('int', 'General', 1), 'USER_AGENT': ('str', 'General', ''), 'RATESTARS': ('bool', 'General', 1), + 'EBOOK_WANTED_FORMATS': ('str', 'Preprocess', 'epub, mobi'), + 'DELETE_OTHER_FORMATS': ('bool', 'Preprocess', 0), + 'EBOOK_CONVERT': ('str', 'Preprocess', 'ebook-convert'), + 'KEEP_OPF': ('bool', 'Preprocess', 1), + 'KEEP_JPG': ('bool', 'Preprocess', 1), + 'FFMPEG': ('str', 'Preprocess', 'ffmpeg'), + 'AUDIO_OPTIONS': ('str', 'Preprocess', '-ab, 320k'), + 'CREATE_SINGLEAUDIO': ('bool', 'Preprocess', 0), + 'KEEP_SEPARATEAUDIO': ('bool', 'Preprocess', 0), + 'WRITE_AUDIOTAGS': ('bool', 'Preprocess', 0), + 'SWAP_COVERPAGE': ('bool', 'Preprocess', 0), # 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'), } diff --git a/lazylibrarian/dbupgrade.py b/lazylibrarian/dbupgrade.py index aa1d61bb..880035a2 100644 --- a/lazylibrarian/dbupgrade.py +++ b/lazylibrarian/dbupgrade.py @@ -109,8 +109,9 @@ def upgrade_needed(): # 57 Add Description to comics and Description/Contributors to comicissues # 58 Ensure Link was added to comicissues (was missing for new installs in v57) # 59 Added per provider seeders instead of global + # 60 Moved preprocessor into main program and disabled old preprocessor - db_current_version = 59 + db_current_version = 60 if db_version < db_current_version: return db_current_version @@ -1715,3 +1716,13 @@ def db_v59(myDB, upgradelog): lazylibrarian.CONFIG['NUMBEROFSEEDERS'] = 0 lazylibrarian.config_write() upgradelog.write("%s v59: complete\n" % time.ctime()) + + +# noinspection PyUnusedLocal +def db_v60(myDB, upgradelog): + if lazylibrarian.CONFIG['IMP_PREPROCESSOR']: + lazylibrarian.UPDATE_MSG = 'The old example_preprocessor is deprecated' + lazylibrarian.UPDATE_MSG += '
it\'s functions are now included in the main program' + lazylibrarian.UPDATE_MSG += '
See new config options in "processing" tab' + time.sleep(30) + upgradelog.write("%s v60: complete\n" % time.ctime()) \ No newline at end of file diff --git a/lazylibrarian/postprocess.py b/lazylibrarian/postprocess.py index 4355faef..e49ba46b 100644 --- a/lazylibrarian/postprocess.py +++ b/lazylibrarian/postprocess.py @@ -55,6 +55,7 @@ from lazylibrarian.importer import addAuthorToDB, addAuthorNameToDB, update_tota from lazylibrarian.librarysync import get_book_info, find_book_in_db, LibraryScan from lazylibrarian.magazinescan import create_id from lazylibrarian.images import createMagCover +from lazylibrarian.preprocessor import preprocess_ebook, preprocess_audio, preprocess_magazine from lazylibrarian.notifiers import notify_download, custom_notify_download try: from deluge_client import DelugeRPCClient @@ -134,7 +135,7 @@ def importMag(source_file=None, title=None, issuenum=None): tempdir = tempfile.mkdtemp() _ = safe_copy(source_file, tempdir) success, dest_file = processDestination(tempdir, dest_path, '', '', - global_name, title, "mag") + global_name, title, "magazine") shutil.rmtree(tempdir, ignore_errors=True) if not success: logger.error("Unable to import %s: %s" % (source_file, dest_file)) @@ -914,6 +915,7 @@ def processDir(reset=False, startdir=None, ignoreclient=False, downloadid=None): else: data = myDB.match('SELECT IssueDate from magazines WHERE Title=?', (book['BookID'],)) if data: # it's a magazine + book_type = 'magazine' logger.debug('Processing magazine %s' % book['BookID']) # AuxInfo was added for magazine release date, normally housed in 'magazines' # but if multiple files are downloading, there will be an error in post-processing @@ -959,6 +961,7 @@ def processDir(reset=False, startdir=None, ignoreclient=False, downloadid=None): data = None if data: # it's a comic + book_type = 'comic' logger.debug('Processing %s issue %s' % (data['Title'], issueid)) mostrecentissue = data['LatestIssue'] if PY2: @@ -2165,11 +2168,23 @@ def processDestination(pp_path=None, dest_path=None, authorname=None, bookname=N return False, 'Unable to locate a valid filetype (%s) in %s, leaving for manual processing' % ( booktype, pp_path) + if booktype == 'ebook': + preprocess_ebook(pp_path) + elif booktype == 'audio': + preprocess_audio(pp_path, authorname, bookname) + elif booktype == 'magazine': + myDB = database.DBConnection() + res = myDB.match("SELECT CoverPage from magazines WHERE Title=?", (bookid,)) + cover = 0 + if res: + cover = check_int(res['CoverPage'], 0) + preprocess_magazine(pp_path, cover=cover) + # run custom pre-processing, for example remove unwanted formats # or force format conversion before sending to calibre - if len(lazylibrarian.CONFIG['IMP_PREPROCESS']): - logger.debug("Running PreProcessor: %s %s %s %s" % (booktype, pp_path, authorname, bookname)) - params = [lazylibrarian.CONFIG['IMP_PREPROCESS'], booktype, pp_path, authorname, bookname] + if len(lazylibrarian.CONFIG['EXT_PREPROCESS']): + logger.debug("Running external PreProcessor: %s %s %s %s" % (booktype, pp_path, authorname, bookname)) + params = [lazylibrarian.CONFIG['EXT_PREPROCESS'], booktype, pp_path, authorname, bookname] rc, res, err = runScript(params) if rc: return False, "Preprocessor returned %s: res[%s] err[%s]" % (rc, res, err) diff --git a/lazylibrarian/preprocessor.py b/lazylibrarian/preprocessor.py new file mode 100644 index 00000000..4392e473 --- /dev/null +++ b/lazylibrarian/preprocessor.py @@ -0,0 +1,348 @@ +# This file is part of Lazylibrarian. +# Lazylibrarian is free software':'you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# Lazylibrarian is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with Lazylibrarian. If not, see . + +from __future__ import print_function +from __future__ import with_statement + +import os +import subprocess + +import lazylibrarian +from lazylibrarian import logger +from lazylibrarian.common import listdir, path_exists +from lazylibrarian.formatter import makeBytestr, check_int, getList + +try: + from tinytag import TinyTag +except ImportError: + try: + from lib.tinytag import TinyTag + except ImportError: + TinyTag = None +try: + # noinspection PyProtectedMember + from PyPDF3 import PdfFileWriter, PdfFileReader +except ImportError: + try: + # noinspection PyProtectedMember + from lib.PyPDF3 import PdfFileWriter, PdfFileReader + except ImportError: + PdfFileWriter = None + PdfFileReader = None + + +def preprocess_ebook(bookfolder): + ebook_convert = lazylibrarian.CONFIG['ebook_convert'] + if not path_exists(ebook_convert): + logger.error("%s not found" % ebook_convert) + return + + logger.debug("Preprocess ebook %s" % bookfolder) + sourcefile = None + created = '' + for fname in listdir(bookfolder): + filename, extn = os.path.splitext(fname) + if extn.lower() == '.epub': + sourcefile = fname + break + elif extn.lower() in ['.mobi', '.azw3']: + sourcefile = fname + break + + logger.debug("Wanted formats: %s" % lazylibrarian.CONFIG['ebook_wanted_formats']) + if not sourcefile: + logger.error("No suitable sourcefile found in %s" % bookfolder) + return + + basename, source_extn = os.path.splitext(sourcefile) + wanted_formats = getList(lazylibrarian.CONFIG['ebook_wanted_formats']) + for ftype in wanted_formats: + if not path_exists(os.path.join(bookfolder, basename + '.' + ftype)): + logger.debug("No %s" % ftype) + params = [ebook_convert, os.path.join(bookfolder, sourcefile), + os.path.join(bookfolder, basename + '.' + ftype)] + if ftype == 'mobi': + params.extend(['--output-profile', 'kindle']) + try: + _ = subprocess.check_output(params, stderr=subprocess.STDOUT) + if created: + created += ' ' + created += ftype + except Exception as e: + logger.error("%s" % e) + logger.error(repr(params)) + return + else: + logger.debug("Found %s" % ftype) + + if lazylibrarian.CONFIG['delete_other_formats']: + if lazylibrarian.CONFIG['keep_opf']: + wanted_formats.append('opf') + if lazylibrarian.CONFIG['keep_opf']: + wanted_formats.append('jpg') + for fname in listdir(bookfolder): + filename, extn = os.path.splitext(fname) + if not extn or extn.lstrip('.').lower() not in wanted_formats: + logger.debug("Deleting %s" % fname) + try: + os.remove(os.path.join(bookfolder, fname)) + except OSError: + pass + if created: + logger.debug("Created %s from %s" % (created, source_extn)) + else: + logger.debug("No extra ebook formats created") + + +def preprocess_audio(bookfolder, authorname, bookname): + if not lazylibrarian.CONFIG['create_singleaudio'] and not lazylibrarian.CONFIG['write_audiotags']: + return + + ffmpeg = lazylibrarian.CONFIG['ffmpeg'] + if not path_exists(ffmpeg): + logger.error("%s not found" % ffmpeg) + return + + if not TinyTag: + logger.error("TinyTag not found") + return + + logger.debug("Preprocess audio %s %s %s" % (bookfolder, authorname, bookname)) + # this produces a single file audiobook + ffmpeg_params = ['-f', 'concat', '-safe', '0', '-i', + os.path.join(bookfolder, 'partslist.ll'), '-f', 'ffmetadata', + '-i', os.path.join(bookfolder, 'metadata.ll'), '-map_metadata', '1', + '-id3v2_version', '3'] + cnt = 0 + parts = [] + total = 0 + author = '' + book = '' + audio_file = '' + out_type = '' + for f in listdir(bookfolder): + extn = os.path.splitext(f)[1].lstrip('.') + if extn.lower() in getList(lazylibrarian.CONFIG['audiobook_type']): + cnt += 1 + audio_file = f + try: + audio_path = os.path.join(bookfolder, f) + performer = '' + composer = '' + albumartist = '' + book = '' + track = 0 + total = 0 + if TinyTag.is_supported(audio_path): + id3r = TinyTag.get(audio_path) + performer = id3r.artist + composer = id3r.composer + albumartist = id3r.albumartist + book = id3r.album + track = id3r.track + total = id3r.track_total + + track = check_int(track, 0) + total = check_int(total, 0) + + if performer: + performer = performer.strip() + if composer: + composer = composer.strip() + if book: + book = book.strip() + if albumartist: + albumartist = albumartist.strip() + + if composer: # if present, should be author + author = composer + elif performer: # author, or narrator if composer == author + author = performer + elif albumartist: + author = albumartist + if author and book: + parts.append([track, book, author, f]) + if track == 1: + out_type = extn + except Exception as e: + logger.debug("tinytag %s %s" % (type(e).__name__, str(e))) + pass + + logger.info("%s found %s audiofiles" % (book, cnt)) + + if cnt == 1 and not parts: # single file audiobook with no tags + parts = [[1, book, author, audio_file]] + + if cnt != len(parts): + logger.error("%s: Incorrect number of parts (found %i from %i)" % (book, len(parts), cnt)) + return + + if total and total != cnt: + logger.error("%s: Reported %i parts, got %i" % (book, total, cnt)) + return + + if cnt == 1: + logger.info("Only one audio file found, nothing to merge") + return + + # check all parts have the same author and title + if len(parts) > 1: + for part in parts: + if part[1] != book: + logger.error("%s: Inconsistent title: [%s][%s]" % (book, part[1], book)) + return + if part[2] != author: + logger.error("%s: Inconsistent author: [%s][%s]" % (book, part[2], author)) + return + + # do we have any track info (value is 0 if not) + tokmatch = '' + if parts[0][0] == 0: + # try to extract part information from filename. Search for token style of part 1 in this order... + for token in [' 001.', ' 01.', ' 1.', ' 001 ', ' 01 ', ' 1 ', '01']: + if tokmatch: + break + for part in parts: + if token in part[3]: + tokmatch = token + break + if tokmatch: # we know the numbering style, get numbers for the other parts + cnt = 0 + while cnt < len(parts): + cnt += 1 + if tokmatch == ' 001.': + pattern = ' %s.' % str(cnt).zfill(3) + elif tokmatch == ' 01.': + pattern = ' %s.' % str(cnt).zfill(2) + elif tokmatch == ' 1.': + pattern = ' %s.' % str(cnt) + elif tokmatch == ' 001 ': + pattern = ' %s ' % str(cnt).zfill(3) + elif tokmatch == ' 01 ': + pattern = ' %s ' % str(cnt).zfill(2) + elif tokmatch == ' 1 ': + pattern = ' %s ' % str(cnt) + else: + pattern = '%s' % str(cnt).zfill(2) + # standardise numbering of the parts + for part in parts: + if pattern in part[3]: + part[0] = cnt + break + + parts.sort(key=lambda x: x[0]) + # check all parts are present + cnt = 0 + while cnt < len(parts): + if parts[cnt][0] != cnt + 1: + logger.error("%s: No part %i found" % (book, cnt + 1)) + return + cnt += 1 + + # if we get here, looks like we have all the parts + with open(os.path.join(bookfolder, 'partslist.ll'), 'wb') as f: + for part in parts: + f.write("file '%s'" % makeBytestr(part[3])) + if lazylibrarian.CONFIG['write_audiotags'] and authorname and bookname: + if tokmatch or (part[2] != authorname) or (part[1] != bookname): + extn = os.path.splitext(part[3])[1] + params = [ffmpeg, '-i', os.path.join(bookfolder, part[3]), + '-y', '-c:a', 'copy', '-metadata', "album=%s" % bookname, + '-metadata', "artist=%s" % authorname, + '-metadata', "track=%s" % part[0], + os.path.join(bookfolder, "tempaudio%s" % extn)] + try: + _ = subprocess.check_output(params, stderr=subprocess.STDOUT) + os.remove(os.path.join(bookfolder, part[3])) + os.rename(os.path.join(bookfolder, "tempaudio%s" % extn), + os.path.join(bookfolder, part[3])) + logger.debug("Metadata written to %s" % part[3]) + except Exception as e: + logger.error(str(e)) + return + + if lazylibrarian.CONFIG['create_singleaudio']: + params = [ffmpeg, '-i', os.path.join(bookfolder, parts[0][3]), + '-f', 'ffmetadata', '-y', os.path.join(bookfolder, 'metadata.ll')] + try: + _ = subprocess.check_output(params, stderr=subprocess.STDOUT) + logger.debug("Metadata written to metadata.ll") + except Exception as e: + logger.error(str(e)) + return + + params = [ffmpeg] + params.extend(ffmpeg_params) + params.extend(getList(lazylibrarian.CONFIG['audio_options'])) + params.append('-y') + if not out_type: + out_type = 'mp3' + outfile = "%s - %s.%s" % (author, book, out_type) + params.append(os.path.join(bookfolder, outfile)) + + try: + logger.debug("Processing %d files" % len(parts)) + _ = subprocess.check_output(params, stderr=subprocess.STDOUT) + except Exception as e: + logger.error(str(e)) + return + + logger.info("%d files merged into %s" % (len(parts), outfile)) + os.remove(os.path.join(bookfolder, 'partslist.ll')) + os.remove(os.path.join(bookfolder, 'metadata.ll')) + if not lazylibrarian.CONFIG['keep_separate_audio']: + logger.debug("Removing %d part files" % len(parts)) + for part in parts: + os.remove(os.path.join(bookfolder, part[3])) + + +def preprocess_magazine(bookfolder, cover=0): + logger.debug("Preprocess magazine %s cover=%s" % (bookfolder, cover)) + if cover < 2: + return + + if not PdfFileWriter: + logger.error("PdfFileWriter not found") + return + + try: + sourcefile = None + for fname in listdir(bookfolder): + filename, extn = os.path.splitext(fname) + if extn.lower() == '.pdf': + sourcefile = fname + break + + if not sourcefile: + logger.error("No suitable sourcefile found in %s" % bookfolder) + return + + cover -= 1 # zero based page count + fname = os.path.join(bookfolder, sourcefile) + output = PdfFileWriter() + f = open(fname, "rb") + input1 = PdfFileReader(f) + cnt = input1.getNumPages() + output.addPage(input1.getPage(cover)) + p = 0 + while p < cnt: + if p != cover: + output.addPage(input1.getPage(p)) + p = p + 1 + with open(fname + 'new', "wb") as outputStream: + output.write(outputStream) + logger.debug("%s has %d pages. Cover from page %d" % (fname, cnt, cover + 1)) + f.close() + os.remove(fname) + os.rename(fname + 'new', fname) + except Exception as e: + logger.error(str(e)) diff --git a/lazylibrarian/webServe.py b/lazylibrarian/webServe.py index f2714a5a..d9972157 100644 --- a/lazylibrarian/webServe.py +++ b/lazylibrarian/webServe.py @@ -5802,9 +5802,9 @@ class WebInterface(object): threading.currentThread().name = "WEBSERVER" cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store" if 'prg' in kwargs and kwargs['prg']: - lazylibrarian.CONFIG['IMP_PREPROCESS'] = kwargs['prg'] - if len(lazylibrarian.CONFIG['IMP_PREPROCESS']): - params = [lazylibrarian.CONFIG['IMP_PREPROCESS'], 'test', ''] + lazylibrarian.CONFIG['EXT_PREPROCESS'] = kwargs['prg'] + if len(lazylibrarian.CONFIG['EXT_PREPROCESS']): + params = [lazylibrarian.CONFIG['EXT_PREPROCESS'], 'test', ''] rc, res, err = runScript(params) if rc: return "Preprocessor returned %s: res[%s] err[%s]" % (rc, res, err)