mirror of
https://gitlab.com/LazyLibrarian/LazyLibrarian.git
synced 2026-02-06 10:47:15 +00:00
Moved preprocessor into main program
This commit is contained in:
parent
63e63655de
commit
fda2b28162
@ -2280,13 +2280,31 @@
|
||||
<span class="help-block">This will include details for other books by new authors found in wishlists or csv files,<br>
|
||||
or when manually importing a book by a new author</span>
|
||||
</div>
|
||||
<fieldset>
|
||||
<legend>External Programs</legend>
|
||||
<div class="form-group">
|
||||
<label for="imp_preprocess">PreProcessor program:</label>
|
||||
<input type="text" id="imp_preprocess" name="imp_preprocess" value="${lazylibrarian.CONFIG['IMP_PREPROCESS']}" class="form-control">
|
||||
<span class="help-block">Path to preprocessor to run before importing books into the library</span>
|
||||
<label for="ext_preprocess">Additional PreProcessor program:</label>
|
||||
<input type="text" id="ext_preprocess" name="ext_preprocess" value="${lazylibrarian.CONFIG['EXT_PREPROCESS']}" class="form-control">
|
||||
<span class="help-block">Path to additional preprocessor to run before importing books into the library</span>
|
||||
<input type="button" value="Test preprocessor" id="testpreprocessor" class="btn btn-default" />
|
||||
</div>
|
||||
<legend>Calibre</legend>
|
||||
<div class="form-group">
|
||||
<label for="ebook_convert">ebook-convert program:</label>
|
||||
<input type="text" id="ebook_convert" name="ebook_convert" value="${lazylibrarian.CONFIG['EBOOK_CONVERT']}" class="form-control" placeholder="ebook-convert program">
|
||||
<span class="help-block">Path to "ebook-convert" to convert book formats</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="ffmpeg">ffmpeg program:</label>
|
||||
<input type="text" id="ffmpeg" name="ffmpeg" value="${lazylibrarian.CONFIG['FFMPEG']}" class="form-control" placeholder="ffmpeg program">
|
||||
<span class="help-block">Path to "ffmpeg" to merge audiobook parts or add tags</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="git_program">git program:</label>
|
||||
<input type="text" placeholder="git program" id="git_program" name="git_program" value="${lazylibrarian.CONFIG['GIT_PROGRAM']}" class="form-control">
|
||||
<span class="help-block">Git program unless already in your path. Usually leave this blank</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
<legend>Calibre</legend>
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<label for="imp_calibredb">Calibredb import program:</label>
|
||||
@ -2645,11 +2663,6 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<label for="git_program">git program:</label>
|
||||
<input type="text" placeholder="git program" id="git_program" name="git_program" value="${lazylibrarian.CONFIG['GIT_PROGRAM']}" class="form-control">
|
||||
<span class="help-block">Git program unless already in your path. Usually leave this blank</span>
|
||||
</div>
|
||||
%if lazylibrarian.CONFIG['MAG_TAB'] == True:
|
||||
<div class="form-group">
|
||||
<label for="imp_convert">Cover creation program:</label>
|
||||
@ -2679,6 +2692,8 @@
|
||||
Create opf files for magazines</label>
|
||||
</div>
|
||||
%endif
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
%if lazylibrarian.CONFIG['COMIC_TAB'] == True:
|
||||
<div class="checkbox">
|
||||
<%
|
||||
@ -2811,6 +2826,96 @@
|
||||
</div>
|
||||
%endif
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>eBook Conversions</legend>
|
||||
<div class="form-group">
|
||||
<label for="ebook_wanted_formats">eBook wanted formats:</label>
|
||||
<input type="text" id="ebook_wanted_formats" name="ebook_wanted_formats" value="${lazylibrarian.CONFIG['EBOOK_WANTED_FORMATS']}" class="form-control" placeholder="wanted formats">
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<%
|
||||
if lazylibrarian.CONFIG['KEEP_OPF'] == True:
|
||||
checked = 'checked="checked"'
|
||||
else:
|
||||
checked = ''
|
||||
%>
|
||||
<label for="keep_opf" class="control-label">
|
||||
<input type="checkbox" id="keep_opf" name="keep_opf" value="1" ${checked} />
|
||||
Keep downloaded opf</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<%
|
||||
if lazylibrarian.CONFIG['KEEP_JPG'] == True:
|
||||
checked = 'checked="checked"'
|
||||
else:
|
||||
checked = ''
|
||||
%>
|
||||
<label for="keep_jpg" class="control-label">
|
||||
<input type="checkbox" id="keep_jpg" name="keep_jpg" value="1" ${checked} />
|
||||
Keep downloaded jpg</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<%
|
||||
if lazylibrarian.CONFIG['DELETE_OTHER_FORMATS'] == True:
|
||||
checked = 'checked="checked"'
|
||||
else:
|
||||
checked = ''
|
||||
%>
|
||||
<label for="delete_other_formats" class="control-label">
|
||||
<input type="checkbox" id="delete_other_formats" name="delete_other_formats" value="1" ${checked} />
|
||||
Delete unwanted filetypes</label>
|
||||
</div>
|
||||
<legend>AudioBook Conversions</legend>
|
||||
<div class="form-group">
|
||||
<label for="audio_options">ffmpeg options:</label>
|
||||
<input type="text" id="audio_options" name="audio_options" value="${lazylibrarian.CONFIG['AUDIO_OPTIONS']}" class="form-control" placeholder="audio options">
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<%
|
||||
if lazylibrarian.CONFIG['CREATE_SINGLEAUDIO'] == True:
|
||||
checked = 'checked="checked"'
|
||||
else:
|
||||
checked = ''
|
||||
%>
|
||||
<label for="create_singleaudio" class="control-label">
|
||||
<input type="checkbox" id="create_singleaudio" name="create_singleaudio" value="1" ${checked} />
|
||||
Merge audiobook parts into one file</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<%
|
||||
if lazylibrarian.CONFIG['KEEP_SEPARATEAUDIO'] == True:
|
||||
checked = 'checked="checked"'
|
||||
else:
|
||||
checked = ''
|
||||
%>
|
||||
<label for="keep_separateaudio" class="control-label">
|
||||
<input type="checkbox" id="keep_separateaudio" name="keep_separateaudio" value="1" ${checked} />
|
||||
Also keep separate parts</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<%
|
||||
if lazylibrarian.CONFIG['WRITE_AUDIOTAGS'] == True:
|
||||
checked = 'checked="checked"'
|
||||
else:
|
||||
checked = ''
|
||||
%>
|
||||
<label for="write_audiotags" class="control-label">
|
||||
<input type="checkbox" id="write_audiotags" name="write_audiotags" value="1" ${checked} />
|
||||
Write audio tags</label>
|
||||
</div>
|
||||
<legend>Magazine Conversions</legend>
|
||||
<div class="checkbox">
|
||||
<%
|
||||
if lazylibrarian.CONFIG['SWAP_COVERPAGE'] == True:
|
||||
checked = 'checked="checked"'
|
||||
else:
|
||||
checked = ''
|
||||
%>
|
||||
<label for="swap_coverpage" class="control-label">
|
||||
<input type="checkbox" id="swap_coverpage" name="swap_coverpage" value="1" ${checked} />
|
||||
Swap Coverpage</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
@ -3919,7 +4024,7 @@
|
||||
<label for="datetype[${mag['Title']}]" class="input-group-addon">Date Style</label>
|
||||
<input type="text" id="datetype[${mag['Title']}]" name="datetype[${mag['Title']}]" value="${mag['DateType']}" class="form-control" placeholder="auto">
|
||||
<label for="coverpage[${mag['Title']}]" class="input-group-addon">Cover Page</label>
|
||||
<input type="number" id="coverpage[${mag['Title']}]" name="coverpage[${mag['Title']}]" value="${mag['CoverPage']}" class="form-control" placeholder="1">
|
||||
<input type="number" id="coverpage[${mag['Title']}]" name="coverpage[${mag['Title']}]" value="${mag['CoverPage']}" min=1 class="form-control" placeholder="1">
|
||||
</div>
|
||||
</div>
|
||||
%endfor
|
||||
|
||||
@ -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({
|
||||
|
||||
5
example_preprocessor.py → demo_preprocessor.py
Executable file → Normal file
5
example_preprocessor.py → demo_preprocessor.py
Executable file → Normal file
@ -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
|
||||
@ -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'),
|
||||
}
|
||||
|
||||
|
||||
@ -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 = '<b>The old example_preprocessor is deprecated</b>'
|
||||
lazylibrarian.UPDATE_MSG += '<br>it\'s functions are now included in the main program'
|
||||
lazylibrarian.UPDATE_MSG += '<br>See new config options in "processing" tab'
|
||||
time.sleep(30)
|
||||
upgradelog.write("%s v60: complete\n" % time.ctime())
|
||||
@ -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)
|
||||
|
||||
348
lazylibrarian/preprocessor.py
Normal file
348
lazylibrarian/preprocessor.py
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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))
|
||||
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user