Files
nhentai/doujinshi_dl/command.py
Ricter Zheng 51fd1f964c fix: skip post-processing when CBZ/PDF already exists, still update history DB
When check_if_need_download() returns False (CBZ/PDF file already exists),
skip all post-processing steps that require doujinshi_dir to be present
(serializer, generate_doc, generate_html, rm_origin_dir, etc.), as the
directory may have been removed by --rm-origin-dir on a previous run.

However, still write to the download history DB unconditionally so that
records lost due to DB corruption/reset are recovered on the next run.

Fixes #429

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 09:38:56 +08:00

203 lines
6.8 KiB
Python

# coding: utf-8
import os
import shutil
import sys
import signal
import platform
import urllib3.exceptions
from doujinshi_dl.cmdline import cmd_parser, banner, write_config
from doujinshi_dl.core.registry import get_first_plugin
from doujinshi_dl.core import config as core_config
from doujinshi_dl.downloader import Downloader, CompressedDownloader
from doujinshi_dl.logger import logger
from doujinshi_dl.utils import (
generate_html, generate_doc, generate_main_html,
paging, signal_handler, DB, move_to_folder,
)
def main():
banner()
if sys.version_info < (3, 0, 0):
logger.error('doujinshi-dl requires Python 3.x')
sys.exit(1)
plugin = get_first_plugin()
parser = plugin.create_parser()
serializer = plugin.create_serializer()
options = cmd_parser()
# Let the plugin configure its own CONFIG and register runtime values
# (this also sets core_config 'base_url' from env or plugin default)
plugin.configure(options)
# Read common config values registered by the plugin
plugin_config = core_config.get('plugin_config', {})
base_url = core_config.get('base_url', os.getenv('DOUJINSHI_DL_URL', ''))
if not base_url:
logger.error('No target URL configured. Set DOUJINSHI_DL_URL or install a plugin that provides a default URL.')
sys.exit(1)
logger.info(f'Using mirror: {base_url}')
# CONFIG['proxy'] may have been updated after cmd_parser()
proxy = plugin_config.get('proxy', '')
if proxy:
if isinstance(proxy, dict):
proxy = proxy.get('http', '')
plugin_config['proxy'] = proxy
logger.warning(f'Update proxy config to: {proxy}')
write_config()
logger.info(f'Using proxy: {proxy}')
if not plugin_config.get('template'):
plugin_config['template'] = 'default'
template = plugin_config.get('template', 'default')
language = plugin_config.get('language', '')
logger.info(f'Using viewer template "{template}"')
# Check authentication
plugin.check_auth()
doujinshis = []
doujinshi_ids = []
page_list = paging(options.page)
if options.favorites:
if not options.is_download:
logger.warning('You do not specify --download option')
doujinshis = parser.favorites(page=page_list) if options.page else parser.favorites()
elif options.keyword:
if language:
logger.info(f'Using default language: {language}')
options.keyword += f' language:{language}'
doujinshis = parser.search(
options.keyword,
sorting=options.sorting,
page=page_list,
is_page_all=options.page_all,
)
elif options.artist:
doujinshis = parser.search(
f'artist:{options.artist}',
sorting=options.sorting,
page=page_list,
is_page_all=options.page_all,
)
elif not doujinshi_ids:
doujinshi_ids = options.id
plugin.print_results(doujinshis)
if options.is_download and doujinshis:
doujinshi_ids = [i['id'] for i in doujinshis]
if options.is_save_download_history:
with DB() as db:
data = set(map(int, db.get_all()))
doujinshi_ids = list(set(map(int, doujinshi_ids)) - set(data))
logger.info(f'New doujinshis account: {len(doujinshi_ids)}')
if options.zip:
options.is_nohtml = True
if not options.is_show:
downloader = (CompressedDownloader if options.zip else Downloader)(
path=options.output_dir,
threads=options.threads,
timeout=options.timeout,
delay=options.delay,
exit_on_fail=options.exit_on_fail,
no_filename_padding=options.no_filename_padding,
)
for doujinshi_id in doujinshi_ids:
meta = parser.fetch(str(doujinshi_id))
if not meta:
continue
doujinshi_model = plugin.create_model(meta, name_format=options.name_format)
doujinshi = doujinshi_model.doujinshi
doujinshi.downloader = downloader
need_download = doujinshi.check_if_need_download(options)
if need_download:
doujinshi.download()
else:
logger.info(
f'Skip download doujinshi because a PDF/CBZ file exists of doujinshi {doujinshi.name}'
)
doujinshi_dir = os.path.join(options.output_dir, doujinshi.filename)
# If skipped (CBZ/PDF already exists), treat as already downloaded:
# - skip all post-processing that requires doujinshi_dir to exist
# - but still write to history DB in case the record was lost
if options.is_save_download_history:
with DB() as db:
db.add_one(doujinshi.id)
if not need_download:
continue
if options.generate_metadata:
serializer.write_all(meta, doujinshi_dir)
logger.log(16, f'Metadata files have been written to "{doujinshi_dir}"')
if not options.is_nohtml:
generate_html(options.output_dir, doujinshi, template=template)
if options.is_cbz:
# Write ComicInfo.xml metadata before packaging
serializer.write_all(meta, doujinshi_dir)
generate_doc('cbz', options.output_dir, doujinshi, options.regenerate)
if options.is_pdf:
generate_doc('pdf', options.output_dir, doujinshi, options.regenerate)
if options.move_to_folder:
if options.is_cbz:
move_to_folder(options.output_dir, doujinshi, 'cbz')
if options.is_pdf:
move_to_folder(options.output_dir, doujinshi, 'pdf')
if options.rm_origin_dir:
if options.move_to_folder:
logger.critical('You specified both --move-to-folder and --rm-origin-dir options, '
'you will not get anything :(')
shutil.rmtree(os.path.join(options.output_dir, doujinshi.filename), ignore_errors=True)
if options.main_viewer:
generate_main_html(options.output_dir)
serializer.finalize(options.output_dir)
if not platform.system() == 'Windows':
logger.log(16, '🍻 All done.')
else:
logger.log(16, 'All done.')
else:
for doujinshi_id in doujinshi_ids:
meta = parser.fetch(str(doujinshi_id))
if not meta:
continue
doujinshi_model = plugin.create_model(meta, name_format=options.name_format)
doujinshi = doujinshi_model.doujinshi
doujinshi.show()
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
signal.signal(signal.SIGINT, signal_handler)
if __name__ == '__main__':
main()