From 14b3c82248baba4b70280dcb6256983d0b35b3a8 Mon Sep 17 00:00:00 2001 From: Ricter Z Date: Sat, 11 Aug 2018 09:28:39 +0800 Subject: [PATCH] remove \r --- nhentai/__init__.py | 6 +- nhentai/cmdline.py | 240 ++++++++++++++++----------------- nhentai/command.py | 144 ++++++++++---------- nhentai/parser.py | 316 ++++++++++++++++++++++---------------------- nhentai/utils.py | 230 ++++++++++++++++---------------- 5 files changed, 468 insertions(+), 468 deletions(-) diff --git a/nhentai/__init__.py b/nhentai/__init__.py index 51111f2..5a6eaa9 100644 --- a/nhentai/__init__.py +++ b/nhentai/__init__.py @@ -1,3 +1,3 @@ -__version__ = '0.2.14' -__author__ = 'RicterZ' -__email__ = 'ricterzheng@gmail.com' +__version__ = '0.2.14' +__author__ = 'RicterZ' +__email__ = 'ricterzheng@gmail.com' diff --git a/nhentai/cmdline.py b/nhentai/cmdline.py index deea5b2..d3453d2 100644 --- a/nhentai/cmdline.py +++ b/nhentai/cmdline.py @@ -1,120 +1,120 @@ -# coding: utf-8 -from __future__ import print_function -import sys -from optparse import OptionParser -from nhentai import __version__ -try: - from itertools import ifilter as filter -except ImportError: - pass - -import nhentai.constant as constant -from nhentai.utils import urlparse, generate_html -from nhentai.logger import logger - -try: - reload(sys) - sys.setdefaultencoding(sys.stdin.encoding) -except NameError: - # python3 - pass - - -def banner(): - logger.info(u'''nHentai ver %s: あなたも変態。 いいね? - _ _ _ _ - _ __ | | | | ___ _ __ | |_ __ _(_) -| '_ \| |_| |/ _ \ '_ \| __/ _` | | -| | | | _ | __/ | | | || (_| | | -|_| |_|_| |_|\___|_| |_|\__\__,_|_| -''' % __version__) - - -def cmd_parser(): - parser = OptionParser('\n nhentai --search [keyword] --download' - '\n NHENTAI=http://h.loli.club nhentai --id [ID ...]' - '\n\nEnvironment Variable:\n' - ' NHENTAI nhentai mirror url') - parser.add_option('--download', dest='is_download', action='store_true', - help='download doujinshi (for search result)') - parser.add_option('--show-info', dest='is_show', action='store_true', help='just show the doujinshi information') - parser.add_option('--id', type='string', dest='id', action='store', help='doujinshi ids set, e.g. 1,2,3') - parser.add_option('--search', type='string', dest='keyword', action='store', help='search doujinshi by keyword') - parser.add_option('--page', type='int', dest='page', action='store', default=1, - help='page number of search result') - parser.add_option('--tags', type='string', dest='tags', action='store', help='download doujinshi by tags') - parser.add_option('--output', type='string', dest='output_dir', action='store', default='', - help='output dir') - parser.add_option('--threads', '-t', type='int', dest='threads', action='store', default=5, - help='thread count of download doujinshi') - parser.add_option('--timeout', type='int', dest='timeout', action='store', default=30, - help='timeout of download doujinshi') - parser.add_option('--proxy', type='string', dest='proxy', action='store', default='', - help='use proxy, example: http://127.0.0.1:1080') - parser.add_option('--html', dest='html_viewer', action='store_true', - help='generate a html viewer at current directory') - - parser.add_option('--login', '-l', type='str', dest='login', action='store', - help='username:password pair of nhentai account') - - parser.add_option('--nohtml', dest='is_nohtml', action='store_true', - help='Don\'t generate HTML') - - parser.add_option('--cbz', dest='is_cbz', action='store_true', - help='Generate Comic Book CBZ File') - - try: - sys.argv = list(map(lambda x: unicode(x.decode(sys.stdin.encoding)), sys.argv)) - except (NameError, TypeError): - pass - except UnicodeDecodeError: - exit(0) - - args, _ = parser.parse_args(sys.argv[1:]) - - if args.html_viewer: - generate_html() - exit(0) - - if args.login: - try: - _, _ = args.login.split(':', 1) - except ValueError: - logger.error('Invalid `username:password` pair.') - exit(1) - - if not args.is_download: - logger.warning('YOU DO NOT SPECIFY `--download` OPTION !!!') - - if args.tags: - logger.warning('`--tags` is under construction') - exit(1) - - if args.id: - _ = map(lambda id: id.strip(), args.id.split(',')) - args.id = set(map(int, filter(lambda id_: id_.isdigit(), _))) - - if (args.is_download or args.is_show) and not args.id and not args.keyword and not args.login: - logger.critical('Doujinshi id(s) are required for downloading') - parser.print_help() - exit(1) - - if not args.keyword and not args.id and not args.login: - parser.print_help() - exit(1) - - if args.threads <= 0: - args.threads = 1 - - elif args.threads > 15: - logger.critical('Maximum number of used threads is 15') - exit(1) - - if args.proxy: - proxy_url = urlparse(args.proxy) - if proxy_url.scheme not in ('http', 'https'): - logger.error('Invalid protocol \'{0}\' of proxy, ignored'.format(proxy_url.scheme)) - else: - constant.PROXY = {'http': args.proxy, 'https': args.proxy} - - return args +# coding: utf-8 +from __future__ import print_function +import sys +from optparse import OptionParser +from nhentai import __version__ +try: + from itertools import ifilter as filter +except ImportError: + pass + +import nhentai.constant as constant +from nhentai.utils import urlparse, generate_html +from nhentai.logger import logger + +try: + reload(sys) + sys.setdefaultencoding(sys.stdin.encoding) +except NameError: + # python3 + pass + + +def banner(): + logger.info(u'''nHentai ver %s: あなたも変態。 いいね? + _ _ _ _ + _ __ | | | | ___ _ __ | |_ __ _(_) +| '_ \| |_| |/ _ \ '_ \| __/ _` | | +| | | | _ | __/ | | | || (_| | | +|_| |_|_| |_|\___|_| |_|\__\__,_|_| +''' % __version__) + + +def cmd_parser(): + parser = OptionParser('\n nhentai --search [keyword] --download' + '\n NHENTAI=http://h.loli.club nhentai --id [ID ...]' + '\n\nEnvironment Variable:\n' + ' NHENTAI nhentai mirror url') + parser.add_option('--download', dest='is_download', action='store_true', + help='download doujinshi (for search result)') + parser.add_option('--show-info', dest='is_show', action='store_true', help='just show the doujinshi information') + parser.add_option('--id', type='string', dest='id', action='store', help='doujinshi ids set, e.g. 1,2,3') + parser.add_option('--search', type='string', dest='keyword', action='store', help='search doujinshi by keyword') + parser.add_option('--page', type='int', dest='page', action='store', default=1, + help='page number of search result') + parser.add_option('--tags', type='string', dest='tags', action='store', help='download doujinshi by tags') + parser.add_option('--output', type='string', dest='output_dir', action='store', default='', + help='output dir') + parser.add_option('--threads', '-t', type='int', dest='threads', action='store', default=5, + help='thread count of download doujinshi') + parser.add_option('--timeout', type='int', dest='timeout', action='store', default=30, + help='timeout of download doujinshi') + parser.add_option('--proxy', type='string', dest='proxy', action='store', default='', + help='use proxy, example: http://127.0.0.1:1080') + parser.add_option('--html', dest='html_viewer', action='store_true', + help='generate a html viewer at current directory') + + parser.add_option('--login', '-l', type='str', dest='login', action='store', + help='username:password pair of nhentai account') + + parser.add_option('--nohtml', dest='is_nohtml', action='store_true', + help='Don\'t generate HTML') + + parser.add_option('--cbz', dest='is_cbz', action='store_true', + help='Generate Comic Book CBZ File') + + try: + sys.argv = list(map(lambda x: unicode(x.decode(sys.stdin.encoding)), sys.argv)) + except (NameError, TypeError): + pass + except UnicodeDecodeError: + exit(0) + + args, _ = parser.parse_args(sys.argv[1:]) + + if args.html_viewer: + generate_html() + exit(0) + + if args.login: + try: + _, _ = args.login.split(':', 1) + except ValueError: + logger.error('Invalid `username:password` pair.') + exit(1) + + if not args.is_download: + logger.warning('YOU DO NOT SPECIFY `--download` OPTION !!!') + + if args.tags: + logger.warning('`--tags` is under construction') + exit(1) + + if args.id: + _ = map(lambda id: id.strip(), args.id.split(',')) + args.id = set(map(int, filter(lambda id_: id_.isdigit(), _))) + + if (args.is_download or args.is_show) and not args.id and not args.keyword and not args.login: + logger.critical('Doujinshi id(s) are required for downloading') + parser.print_help() + exit(1) + + if not args.keyword and not args.id and not args.login: + parser.print_help() + exit(1) + + if args.threads <= 0: + args.threads = 1 + + elif args.threads > 15: + logger.critical('Maximum number of used threads is 15') + exit(1) + + if args.proxy: + proxy_url = urlparse(args.proxy) + if proxy_url.scheme not in ('http', 'https'): + logger.error('Invalid protocol \'{0}\' of proxy, ignored'.format(proxy_url.scheme)) + else: + constant.PROXY = {'http': args.proxy, 'https': args.proxy} + + return args diff --git a/nhentai/command.py b/nhentai/command.py index cfbf2ad..5f08cef 100644 --- a/nhentai/command.py +++ b/nhentai/command.py @@ -1,72 +1,72 @@ -#!/usr/bin/env python2.7 -# coding: utf-8 -from __future__ import unicode_literals, print_function -import signal -import platform - -from nhentai.cmdline import cmd_parser, banner -from nhentai.parser import doujinshi_parser, search_parser, print_doujinshi, login_parser -from nhentai.doujinshi import Doujinshi -from nhentai.downloader import Downloader -from nhentai.logger import logger -from nhentai.constant import BASE_URL -from nhentai.utils import generate_html, generate_cbz - - -def main(): - banner() - logger.info('Using mirror: {0}'.format(BASE_URL)) - options = cmd_parser() - - doujinshi_ids = [] - doujinshi_list = [] - - if options.login: - username, password = options.login.split(':', 1) - logger.info('Login to nhentai use credential \'%s:%s\'' % (username, '*' * len(password))) - for doujinshi_info in login_parser(username=username, password=password): - doujinshi_list.append(Doujinshi(**doujinshi_info)) - - if options.keyword: - doujinshis = search_parser(options.keyword, options.page) - print_doujinshi(doujinshis) - if options.is_download: - doujinshi_ids = map(lambda d: d['id'], doujinshis) - else: - doujinshi_ids = options.id - - if doujinshi_ids: - for id_ in doujinshi_ids: - doujinshi_info = doujinshi_parser(id_) - doujinshi_list.append(Doujinshi(**doujinshi_info)) - - if not options.is_show: - downloader = Downloader(path=options.output_dir, - thread=options.threads, timeout=options.timeout) - - for doujinshi in doujinshi_list: - doujinshi.downloader = downloader - doujinshi.download() - if not options.is_nohtml and not options.is_cbz: - generate_html(options.output_dir, doujinshi) - elif options.is_cbz: - generate_cbz(options.output_dir, doujinshi) - - if not platform.system() == 'Windows': - logger.log(15, '🍻 All done.') - else: - logger.log(15, 'All done.') - - else: - [doujinshi.show() for doujinshi in doujinshi_list] - - -def signal_handler(signal, frame): - logger.error('Ctrl-C signal received. Quit.') - exit(1) - - -signal.signal(signal.SIGINT, signal_handler) - -if __name__ == '__main__': - main() +#!/usr/bin/env python2.7 +# coding: utf-8 +from __future__ import unicode_literals, print_function +import signal +import platform + +from nhentai.cmdline import cmd_parser, banner +from nhentai.parser import doujinshi_parser, search_parser, print_doujinshi, login_parser +from nhentai.doujinshi import Doujinshi +from nhentai.downloader import Downloader +from nhentai.logger import logger +from nhentai.constant import BASE_URL +from nhentai.utils import generate_html, generate_cbz + + +def main(): + banner() + logger.info('Using mirror: {0}'.format(BASE_URL)) + options = cmd_parser() + + doujinshi_ids = [] + doujinshi_list = [] + + if options.login: + username, password = options.login.split(':', 1) + logger.info('Login to nhentai use credential \'%s:%s\'' % (username, '*' * len(password))) + for doujinshi_info in login_parser(username=username, password=password): + doujinshi_list.append(Doujinshi(**doujinshi_info)) + + if options.keyword: + doujinshis = search_parser(options.keyword, options.page) + print_doujinshi(doujinshis) + if options.is_download: + doujinshi_ids = map(lambda d: d['id'], doujinshis) + else: + doujinshi_ids = options.id + + if doujinshi_ids: + for id_ in doujinshi_ids: + doujinshi_info = doujinshi_parser(id_) + doujinshi_list.append(Doujinshi(**doujinshi_info)) + + if not options.is_show: + downloader = Downloader(path=options.output_dir, + thread=options.threads, timeout=options.timeout) + + for doujinshi in doujinshi_list: + doujinshi.downloader = downloader + doujinshi.download() + if not options.is_nohtml and not options.is_cbz: + generate_html(options.output_dir, doujinshi) + elif options.is_cbz: + generate_cbz(options.output_dir, doujinshi) + + if not platform.system() == 'Windows': + logger.log(15, '🍻 All done.') + else: + logger.log(15, 'All done.') + + else: + [doujinshi.show() for doujinshi in doujinshi_list] + + +def signal_handler(signal, frame): + logger.error('Ctrl-C signal received. Quit.') + exit(1) + + +signal.signal(signal.SIGINT, signal_handler) + +if __name__ == '__main__': + main() diff --git a/nhentai/parser.py b/nhentai/parser.py index d01a7e1..bef5730 100644 --- a/nhentai/parser.py +++ b/nhentai/parser.py @@ -1,158 +1,158 @@ -# coding: utf-8 -from __future__ import unicode_literals, print_function - -import os -import re -import threadpool -import requests -from bs4 import BeautifulSoup -from tabulate import tabulate - -import nhentai.constant as constant -from nhentai.logger import logger - - -def request(method, url, **kwargs): - if not hasattr(requests, method): - raise AttributeError('\'requests\' object has no attribute \'{0}\''.format(method)) - - return requests.__dict__[method](url, proxies=constant.PROXY, verify=False, **kwargs) - - -def login_parser(username, password): - s = requests.Session() - s.proxies = constant.PROXY - s.verify = False - s.headers.update({'Referer': constant.LOGIN_URL}) - - s.get(constant.LOGIN_URL) - content = s.get(constant.LOGIN_URL).content - html = BeautifulSoup(content, 'html.parser') - csrf_token_elem = html.find('input', attrs={'name': 'csrfmiddlewaretoken'}) - - if not csrf_token_elem: - raise Exception('Cannot find csrf token to login') - csrf_token = csrf_token_elem.attrs['value'] - - login_dict = { - 'csrfmiddlewaretoken': csrf_token, - 'username_or_email': username, - 'password': password, - } - resp = s.post(constant.LOGIN_URL, data=login_dict) - if 'Invalid username (or email) or password' in resp.text: - logger.error('Login failed, please check your username and password') - exit(1) - - html = BeautifulSoup(s.get(constant.FAV_URL).content, 'html.parser') - count = html.find('span', attrs={'class': 'count'}) - if not count: - logger.error('Cannot get count of your favorites, maybe login failed.') - - count = int(count.text.strip('(').strip(')')) - if count == 0: - logger.warning('No favorites found') - return [] - pages = int(count / 25) - - if pages: - pages += 1 if count % (25 * pages) else 0 - else: - pages = 1 - - logger.info('Your have %d favorites in %d pages.' % (count, pages)) - - if os.getenv('DEBUG'): - pages = 1 - - ret = [] - doujinshi_id = re.compile('data-id="([\d]+)"') - - def _callback(request, result): - ret.append(result) - - thread_pool = threadpool.ThreadPool(5) - - for page in range(1, pages+1): - try: - logger.info('Getting doujinshi id of page %d' % page) - resp = s.get(constant.FAV_URL + '?page=%d' % page).text - ids = doujinshi_id.findall(resp) - requests_ = threadpool.makeRequests(doujinshi_parser, ids, _callback) - [thread_pool.putRequest(req) for req in requests_] - thread_pool.wait() - except Exception as e: - logger.error('Error: %s, continue', str(e)) - - return ret - - -def doujinshi_parser(id_): - if not isinstance(id_, (int,)) and (isinstance(id_, (str,)) and not id_.isdigit()): - raise Exception('Doujinshi id({0}) is not valid'.format(id_)) - - id_ = int(id_) - logger.log(15, 'Fetching doujinshi information of id {0}'.format(id_)) - doujinshi = dict() - doujinshi['id'] = id_ - url = '{0}/{1}'.format(constant.DETAIL_URL, id_) - - try: - response = request('get', url).json() - except Exception as e: - logger.critical(str(e)) - exit(1) - - doujinshi['name'] = response['title']['english'] - doujinshi['subtitle'] = response['title']['japanese'] - doujinshi['img_id'] = response['media_id'] - doujinshi['ext'] = ''.join(map(lambda s: s['t'], response['images']['pages'])) - doujinshi['pages'] = len(response['images']['pages']) - - # gain information of the doujinshi - needed_fields = ['character', 'artist', 'language'] - for tag in response['tags']: - tag_type = tag['type'] - if tag_type in needed_fields: - if tag_type not in doujinshi: - doujinshi[tag_type] = tag['name'] - else: - doujinshi[tag_type] += tag['name'] - - return doujinshi - - -def search_parser(keyword, page): - logger.debug('Searching doujinshis of keyword {0}'.format(keyword)) - result = [] - try: - response = request('get', url=constant.SEARCH_URL, params={'query': keyword, 'page': page}).json() - if 'result' not in response: - raise Exception('No result in response') - except requests.ConnectionError as e: - logger.critical(e) - logger.warn('If you are in China, please configure the proxy to fu*k GFW.') - exit(1) - - for row in response['result']: - title = row['title']['english'] - title = title[:85] + '..' if len(title) > 85 else title - result.append({'id': row['id'], 'title': title}) - - if not result: - logger.warn('Not found anything of keyword {}'.format(keyword)) - - return result - - -def print_doujinshi(doujinshi_list): - if not doujinshi_list: - return - doujinshi_list = [(i['id'], i['title']) for i in doujinshi_list] - headers = ['id', 'doujinshi'] - logger.info('Search Result\n' + - tabulate(tabular_data=doujinshi_list, headers=headers, tablefmt='rst')) - - -if __name__ == '__main__': - print(doujinshi_parser("32271")) +# coding: utf-8 +from __future__ import unicode_literals, print_function + +import os +import re +import threadpool +import requests +from bs4 import BeautifulSoup +from tabulate import tabulate + +import nhentai.constant as constant +from nhentai.logger import logger + + +def request(method, url, **kwargs): + if not hasattr(requests, method): + raise AttributeError('\'requests\' object has no attribute \'{0}\''.format(method)) + + return requests.__dict__[method](url, proxies=constant.PROXY, verify=False, **kwargs) + + +def login_parser(username, password): + s = requests.Session() + s.proxies = constant.PROXY + s.verify = False + s.headers.update({'Referer': constant.LOGIN_URL}) + + s.get(constant.LOGIN_URL) + content = s.get(constant.LOGIN_URL).content + html = BeautifulSoup(content, 'html.parser') + csrf_token_elem = html.find('input', attrs={'name': 'csrfmiddlewaretoken'}) + + if not csrf_token_elem: + raise Exception('Cannot find csrf token to login') + csrf_token = csrf_token_elem.attrs['value'] + + login_dict = { + 'csrfmiddlewaretoken': csrf_token, + 'username_or_email': username, + 'password': password, + } + resp = s.post(constant.LOGIN_URL, data=login_dict) + if 'Invalid username (or email) or password' in resp.text: + logger.error('Login failed, please check your username and password') + exit(1) + + html = BeautifulSoup(s.get(constant.FAV_URL).content, 'html.parser') + count = html.find('span', attrs={'class': 'count'}) + if not count: + logger.error('Cannot get count of your favorites, maybe login failed.') + + count = int(count.text.strip('(').strip(')')) + if count == 0: + logger.warning('No favorites found') + return [] + pages = int(count / 25) + + if pages: + pages += 1 if count % (25 * pages) else 0 + else: + pages = 1 + + logger.info('Your have %d favorites in %d pages.' % (count, pages)) + + if os.getenv('DEBUG'): + pages = 1 + + ret = [] + doujinshi_id = re.compile('data-id="([\d]+)"') + + def _callback(request, result): + ret.append(result) + + thread_pool = threadpool.ThreadPool(5) + + for page in range(1, pages+1): + try: + logger.info('Getting doujinshi id of page %d' % page) + resp = s.get(constant.FAV_URL + '?page=%d' % page).text + ids = doujinshi_id.findall(resp) + requests_ = threadpool.makeRequests(doujinshi_parser, ids, _callback) + [thread_pool.putRequest(req) for req in requests_] + thread_pool.wait() + except Exception as e: + logger.error('Error: %s, continue', str(e)) + + return ret + + +def doujinshi_parser(id_): + if not isinstance(id_, (int,)) and (isinstance(id_, (str,)) and not id_.isdigit()): + raise Exception('Doujinshi id({0}) is not valid'.format(id_)) + + id_ = int(id_) + logger.log(15, 'Fetching doujinshi information of id {0}'.format(id_)) + doujinshi = dict() + doujinshi['id'] = id_ + url = '{0}/{1}'.format(constant.DETAIL_URL, id_) + + try: + response = request('get', url).json() + except Exception as e: + logger.critical(str(e)) + exit(1) + + doujinshi['name'] = response['title']['english'] + doujinshi['subtitle'] = response['title']['japanese'] + doujinshi['img_id'] = response['media_id'] + doujinshi['ext'] = ''.join(map(lambda s: s['t'], response['images']['pages'])) + doujinshi['pages'] = len(response['images']['pages']) + + # gain information of the doujinshi + needed_fields = ['character', 'artist', 'language'] + for tag in response['tags']: + tag_type = tag['type'] + if tag_type in needed_fields: + if tag_type not in doujinshi: + doujinshi[tag_type] = tag['name'] + else: + doujinshi[tag_type] += tag['name'] + + return doujinshi + + +def search_parser(keyword, page): + logger.debug('Searching doujinshis of keyword {0}'.format(keyword)) + result = [] + try: + response = request('get', url=constant.SEARCH_URL, params={'query': keyword, 'page': page}).json() + if 'result' not in response: + raise Exception('No result in response') + except requests.ConnectionError as e: + logger.critical(e) + logger.warn('If you are in China, please configure the proxy to fu*k GFW.') + exit(1) + + for row in response['result']: + title = row['title']['english'] + title = title[:85] + '..' if len(title) > 85 else title + result.append({'id': row['id'], 'title': title}) + + if not result: + logger.warn('Not found anything of keyword {}'.format(keyword)) + + return result + + +def print_doujinshi(doujinshi_list): + if not doujinshi_list: + return + doujinshi_list = [(i['id'], i['title']) for i in doujinshi_list] + headers = ['id', 'doujinshi'] + logger.info('Search Result\n' + + tabulate(tabular_data=doujinshi_list, headers=headers, tablefmt='rst')) + + +if __name__ == '__main__': + print(doujinshi_parser("32271")) diff --git a/nhentai/utils.py b/nhentai/utils.py index d83ce0f..3cddd50 100644 --- a/nhentai/utils.py +++ b/nhentai/utils.py @@ -1,115 +1,115 @@ -# coding: utf-8 -from __future__ import unicode_literals, print_function - -import os -import string -import zipfile -import shutil -from nhentai.logger import logger - - -class _Singleton(type): - """ A metaclass that creates a Singleton base class when called. """ - _instances = {} - - def __call__(cls, *args, **kwargs): - if cls not in cls._instances: - cls._instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs) - return cls._instances[cls] - - -class Singleton(_Singleton(str('SingletonMeta'), (object,), {})): - pass - - -def urlparse(url): - try: - from urlparse import urlparse - except ImportError: - from urllib.parse import urlparse - - return urlparse(url) - -def readfile(path): - loc = os.path.dirname(__file__) - - with open(os.path.join(loc, path), 'r') as file: - return file.read() - -def generate_html(output_dir='.', doujinshi_obj=None): - image_html = '' - - if doujinshi_obj is not None: - doujinshi_dir = os.path.join(output_dir, format_filename('%s-%s' % (doujinshi_obj.id, - str(doujinshi_obj.name[:200])))) - else: - doujinshi_dir = '.' - - file_list = os.listdir(doujinshi_dir) - file_list.sort() - - for image in file_list: - if not os.path.splitext(image)[1] in ('.jpg', '.png'): - continue - - image_html += '\n'\ - .format(image) - - html = readfile('viewer/index.html') - css = readfile('viewer/styles.css') - js = readfile('viewer/scripts.js') - - if doujinshi_obj is not None: - title = doujinshi_obj.name - else: - title = 'nHentai HTML Viewer' - - data = html.format(TITLE=title, IMAGES=image_html, SCRIPTS=js, STYLES=css) - with open(os.path.join(doujinshi_dir, 'index.html'), 'w') as f: - f.write(data) - - logger.log(15, 'HTML Viewer has been write to \'{0}\''.format(os.path.join(doujinshi_dir, 'index.html'))) - - -def generate_cbz(output_dir='.', doujinshi_obj=None): - if doujinshi_obj is not None: - doujinshi_dir = os.path.join(output_dir, format_filename('%s-%s' % (doujinshi_obj.id, - str(doujinshi_obj.name[:200])))) - cbz_filename = os.path.join(output_dir, format_filename('%s-%s.cbz' % (doujinshi_obj.id, - str(doujinshi_obj.name[:200])))) - else: - cbz_filename = './doujinshi.cbz' - doujinshi_dir = '.' - - file_list = os.listdir(doujinshi_dir) - file_list.sort() - - with zipfile.ZipFile(cbz_filename, 'w') as cbz_pf: - for image in file_list: - image_path = os.path.join(doujinshi_dir, image) - cbz_pf.write(image_path, image) - - shutil.rmtree(doujinshi_dir, ignore_errors=True) - logger.log(15, 'Comic Book CBZ file has been write to \'{0}\''.format(doujinshi_dir)) - - - - - - - -def format_filename(s): - """Take a string and return a valid filename constructed from the string. -Uses a whitelist approach: any characters not present in valid_chars are -removed. Also spaces are replaced with underscores. - -Note: this method may produce invalid filenames such as ``, `.` or `..` -When I use this method I prepend a date string like '2009_01_15_19_46_32_' -and append a file extension like '.txt', so I avoid the potential of using -an invalid filename. - -""" - valid_chars = "-_.() %s%s" % (string.ascii_letters, string.digits) - filename = ''.join(c for c in s if c in valid_chars) - filename = filename.replace(' ', '_') # I don't like spaces in filenames. - return filename +# coding: utf-8 +from __future__ import unicode_literals, print_function + +import os +import string +import zipfile +import shutil +from nhentai.logger import logger + + +class _Singleton(type): + """ A metaclass that creates a Singleton base class when called. """ + _instances = {} + + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs) + return cls._instances[cls] + + +class Singleton(_Singleton(str('SingletonMeta'), (object,), {})): + pass + + +def urlparse(url): + try: + from urlparse import urlparse + except ImportError: + from urllib.parse import urlparse + + return urlparse(url) + +def readfile(path): + loc = os.path.dirname(__file__) + + with open(os.path.join(loc, path), 'r') as file: + return file.read() + +def generate_html(output_dir='.', doujinshi_obj=None): + image_html = '' + + if doujinshi_obj is not None: + doujinshi_dir = os.path.join(output_dir, format_filename('%s-%s' % (doujinshi_obj.id, + str(doujinshi_obj.name[:200])))) + else: + doujinshi_dir = '.' + + file_list = os.listdir(doujinshi_dir) + file_list.sort() + + for image in file_list: + if not os.path.splitext(image)[1] in ('.jpg', '.png'): + continue + + image_html += '\n'\ + .format(image) + + html = readfile('viewer/index.html') + css = readfile('viewer/styles.css') + js = readfile('viewer/scripts.js') + + if doujinshi_obj is not None: + title = doujinshi_obj.name + else: + title = 'nHentai HTML Viewer' + + data = html.format(TITLE=title, IMAGES=image_html, SCRIPTS=js, STYLES=css) + with open(os.path.join(doujinshi_dir, 'index.html'), 'w') as f: + f.write(data) + + logger.log(15, 'HTML Viewer has been write to \'{0}\''.format(os.path.join(doujinshi_dir, 'index.html'))) + + +def generate_cbz(output_dir='.', doujinshi_obj=None): + if doujinshi_obj is not None: + doujinshi_dir = os.path.join(output_dir, format_filename('%s-%s' % (doujinshi_obj.id, + str(doujinshi_obj.name[:200])))) + cbz_filename = os.path.join(output_dir, format_filename('%s-%s.cbz' % (doujinshi_obj.id, + str(doujinshi_obj.name[:200])))) + else: + cbz_filename = './doujinshi.cbz' + doujinshi_dir = '.' + + file_list = os.listdir(doujinshi_dir) + file_list.sort() + + with zipfile.ZipFile(cbz_filename, 'w') as cbz_pf: + for image in file_list: + image_path = os.path.join(doujinshi_dir, image) + cbz_pf.write(image_path, image) + + shutil.rmtree(doujinshi_dir, ignore_errors=True) + logger.log(15, 'Comic Book CBZ file has been write to \'{0}\''.format(doujinshi_dir)) + + + + + + + +def format_filename(s): + """Take a string and return a valid filename constructed from the string. +Uses a whitelist approach: any characters not present in valid_chars are +removed. Also spaces are replaced with underscores. + +Note: this method may produce invalid filenames such as ``, `.` or `..` +When I use this method I prepend a date string like '2009_01_15_19_46_32_' +and append a file extension like '.txt', so I avoid the potential of using +an invalid filename. + +""" + valid_chars = "-_.() %s%s" % (string.ascii_letters, string.digits) + filename = ''.join(c for c in s if c in valid_chars) + filename = filename.replace(' ', '_') # I don't like spaces in filenames. + return filename