Merge pull request #108 from RicterZ/dev

Merge dev to master
This commit is contained in:
Ricter Zheng 2020-03-14 23:35:02 +08:00 committed by GitHub
commit a5547696eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 665 additions and 292 deletions

View File

@ -74,6 +74,12 @@ Download by tag name:
nhentai --tag lolicon --download --page=2 nhentai --tag lolicon --download --page=2
Download by language:
.. code-block:: bash
nhentai --language english --download --page=2
Download by artist name: Download by artist name:
.. code-block:: bash .. code-block:: bash
@ -84,13 +90,13 @@ Download by character name:
.. code-block:: bash .. code-block:: bash
nhentai --character kuro von einsbern --download nhentai --character "kuro von einsbern" --download
Download by parody name: Download by parody name:
.. code-block:: bash .. code-block:: bash
nhentai --parody the idolmaster --download nhentai --parody "the idolmaster" --download
Download by group name: Download by group name:
@ -102,7 +108,7 @@ Download using multiple tags (--tag, --character, --paordy and --group supported
.. code-block:: bash .. code-block:: bash
nhentai --tag lolicon, teasing --artist tamano kedama, atte nanakusa nhentai --tag "lolicon, teasing" --artist "tamano kedama, atte nanakusa"
Download your favorites with delay: Download your favorites with delay:

View File

@ -55,6 +55,7 @@ def cmd_parser():
parser.add_option('--character', type='string', dest='character', action='store', help='download doujinshi by character') parser.add_option('--character', type='string', dest='character', action='store', help='download doujinshi by character')
parser.add_option('--parody', type='string', dest='parody', action='store', help='download doujinshi by parody') parser.add_option('--parody', type='string', dest='parody', action='store', help='download doujinshi by parody')
parser.add_option('--group', type='string', dest='group', action='store', help='download doujinshi by group') parser.add_option('--group', type='string', dest='group', action='store', help='download doujinshi by group')
parser.add_option('--language', type='string', dest='language', action='store', help='download doujinshi by language')
parser.add_option('--favorites', '-F', action='store_true', dest='favorites', parser.add_option('--favorites', '-F', action='store_true', dest='favorites',
help='list or download your favorites.') help='list or download your favorites.')
@ -115,7 +116,7 @@ def cmd_parser():
if args.main_viewer and not args.id and not args.keyword and \ if args.main_viewer and not args.id and not args.keyword and \
not args.tag and not args.artist and not args.character and \ not args.tag and not args.artist and not args.character and \
not args.parody and not args.group and not args.favorites: not args.parody and not args.group and not args.language and not args.favorites:
generate_main_html() generate_main_html()
exit(0) exit(0)
@ -176,13 +177,13 @@ def cmd_parser():
if (args.is_download or args.is_show) and not args.id and not args.keyword and \ if (args.is_download or args.is_show) and not args.id and not args.keyword and \
not args.tag and not args.artist and not args.character and \ not args.tag and not args.artist and not args.character and \
not args.parody and not args.group and not args.favorites: not args.parody and not args.group and not args.language and not args.favorites:
logger.critical('Doujinshi id(s) are required for downloading') logger.critical('Doujinshi id(s) are required for downloading')
parser.print_help() parser.print_help()
exit(1) exit(1)
if not args.keyword and not args.id and not args.tag and not args.artist and \ if not args.keyword and not args.id and not args.tag and not args.artist and \
not args.character and not args.parody and not args.group and not args.favorites: not args.character and not args.parody and not args.group and not args.language and not args.favorites:
parser.print_help() parser.print_help()
exit(1) exit(1)

View File

@ -70,6 +70,12 @@ def main():
if options.is_download and doujinshis: if options.is_download and doujinshis:
doujinshi_ids = [i['id'] for i in doujinshis] doujinshi_ids = [i['id'] for i in doujinshis]
elif options.language:
doujinshis = tag_parser(options.language, max_page=options.max_page, index=5)
print_doujinshi(doujinshis)
if options.is_download and doujinshis:
doujinshi_ids = [i['id'] for i in doujinshis]
elif options.keyword: elif options.keyword:
doujinshis = search_parser(options.keyword, sorting=options.sorting, page=options.page) doujinshis = search_parser(options.keyword, sorting=options.sorting, page=options.page)
print_doujinshi(doujinshis) print_doujinshi(doujinshis)

View File

@ -21,7 +21,8 @@ TAG_URL = ['%s/tag' % BASE_URL,
'%s/artist' % BASE_URL, '%s/artist' % BASE_URL,
'%s/character' % BASE_URL, '%s/character' % BASE_URL,
'%s/parody' % BASE_URL, '%s/parody' % BASE_URL,
'%s/group' % BASE_URL] '%s/group' % BASE_URL,
'%s/language' % BASE_URL]
TAG_API_URL = '%s/api/galleries/tagged' % BASE_URL TAG_API_URL = '%s/api/galleries/tagged' % BASE_URL
LOGIN_URL = '%s/login/' % BASE_URL LOGIN_URL = '%s/login/' % BASE_URL

View File

@ -163,7 +163,7 @@ def doujinshi_parser(id_):
doujinshi['subtitle'] = subtitle.text if subtitle else '' doujinshi['subtitle'] = subtitle.text if subtitle else ''
doujinshi_cover = html.find('div', attrs={'id': 'cover'}) doujinshi_cover = html.find('div', attrs={'id': 'cover'})
img_id = re.search('/galleries/([\d]+)/cover\.(jpg|png)$', doujinshi_cover.a.img.attrs['data-src']) img_id = re.search('/galleries/([\d]+)/cover\.(jpg|png|gif)$', doujinshi_cover.a.img.attrs['data-src'])
ext = [] ext = []
for i in html.find_all('div', attrs={'class': 'thumb-container'}): for i in html.find_all('div', attrs={'class': 'thumb-container'}):
@ -187,7 +187,7 @@ def doujinshi_parser(id_):
# gain information of the doujinshi # gain information of the doujinshi
information_fields = doujinshi_info.find_all('div', attrs={'class': 'field-name'}) information_fields = doujinshi_info.find_all('div', attrs={'class': 'field-name'})
needed_fields = ['Characters', 'Artists', 'Languages', 'Tags'] needed_fields = ['Characters', 'Artists', 'Languages', 'Tags', 'Parodies', 'Groups', 'Categories']
for field in information_fields: for field in information_fields:
field_name = field.contents[0].strip().strip(':') field_name = field.contents[0].strip().strip(':')
if field_name in needed_fields: if field_name in needed_fields:
@ -195,6 +195,9 @@ def doujinshi_parser(id_):
field.find_all('a', attrs={'class': 'tag'})] field.find_all('a', attrs={'class': 'tag'})]
doujinshi[field_name.lower()] = ', '.join(data) doujinshi[field_name.lower()] = ', '.join(data)
time_field = doujinshi_info.find('time')
if time_field.has_attr('datetime'):
doujinshi['date'] = time_field['datetime']
return doujinshi return doujinshi
@ -224,7 +227,7 @@ def tag_parser(tag_name, sorting='date', max_page=1, index=0):
if ',' in tag_name: if ',' in tag_name:
tag_name = [i.strip().replace(' ', '-') for i in tag_name.split(',')] tag_name = [i.strip().replace(' ', '-') for i in tag_name.split(',')]
else: else:
tag_name = tag_name.replace(' ', '-') tag_name = tag_name.strip().replace(' ', '-')
if sorting == 'date': if sorting == 'date':
sorting = '' sorting = ''
@ -331,7 +334,7 @@ def __api_suspended_doujinshi_parser(id_):
doujinshi['pages'] = len(response['images']['pages']) doujinshi['pages'] = len(response['images']['pages'])
# gain information of the doujinshi # gain information of the doujinshi
needed_fields = ['character', 'artist', 'language', 'tag'] needed_fields = ['character', 'artist', 'language', 'tag', 'parody', 'group', 'category']
for tag in response['tags']: for tag in response['tags']:
tag_type = tag['type'] tag_type = tag['type']
if tag_type in needed_fields: if tag_type in needed_fields:

79
nhentai/serializer.py Normal file
View File

@ -0,0 +1,79 @@
# coding: utf-8
import json
import os
def serialize(doujinshi, dir):
metadata = {'title' : doujinshi.name,
'subtitle' : doujinshi.info.subtitle}
if doujinshi.info.date:
metadata['upload_date'] = doujinshi.info.date
if doujinshi.info.parodies:
metadata['parody'] = [i.strip() for i in doujinshi.info.parodies.split(',')]
if doujinshi.info.characters:
metadata['character'] = [i.strip() for i in doujinshi.info.characters.split(',')]
if doujinshi.info.tags:
metadata['tag'] = [i.strip() for i in doujinshi.info.tags.split(',')]
if doujinshi.info.artists:
metadata['artist'] = [i.strip() for i in doujinshi.info.artists.split(',')]
if doujinshi.info.groups:
metadata['group'] = [i.strip() for i in doujinshi.info.groups.split(',')]
if doujinshi.info.languages:
metadata['language'] = [i.strip() for i in doujinshi.info.languages.split(',')]
metadata['category'] = doujinshi.info.categories
metadata['URL'] = doujinshi.url
metadata['Pages'] = doujinshi.pages
with open(os.path.join(dir, 'metadata.json'), 'w', encoding="raw_unicode_escape") as f:
json.dump(metadata, f, separators=','':')
def merge_json():
lst = []
output_dir = "./"
os.chdir(output_dir)
doujinshi_dirs = next(os.walk('.'))[1]
for folder in doujinshi_dirs:
files = os.listdir(folder)
if 'metadata.json' not in files:
continue
data_folder = output_dir + folder + '/' + 'metadata.json'
json_file = open(data_folder, 'r')
json_dict = {}
json_dict = json.load(json_file)
json_dict['Folder'] = folder
lst.append(json_dict)
return lst
def serialize_unique(lst):
dictionary = {}
parody = []
character = []
tag = []
artist = []
group = []
for dic in lst:
if 'parody' in dic:
parody.extend([i for i in dic['parody']])
if 'character' in dic:
character.extend([i for i in dic['character']])
if 'tag' in dic:
tag.extend([i for i in dic['tag']])
if 'artist' in dic:
artist.extend([i for i in dic['artist']])
if 'group' in dic:
group.extend([i for i in dic['group']])
dictionary['parody'] = list(set(parody))
dictionary['character'] = list(set(character))
dictionary['tag'] = list(set(tag))
dictionary['artist'] = list(set(artist))
dictionary['group'] = list(set(group))
return dictionary
def set_js_database():
with open('data.js', 'w') as f:
indexed_json = merge_json()
unique_json = json.dumps(serialize_unique(indexed_json), separators=','':')
indexed_json = json.dumps(indexed_json, separators=','':')
f.write('var data = '+indexed_json)
f.write(';\nvar tags = '+unique_json)

View File

@ -11,6 +11,7 @@ import requests
from nhentai import constant from nhentai import constant
from nhentai.logger import logger from nhentai.logger import logger
from nhentai.serializer import serialize, set_js_database
def request(method, url, **kwargs): def request(method, url, **kwargs):
@ -79,19 +80,19 @@ def generate_html(output_dir='.', doujinshi_obj=None):
image_html += '<img src="{0}" class="image-item"/>\n'\ image_html += '<img src="{0}" class="image-item"/>\n'\
.format(image) .format(image)
html = readfile('viewer/index.html') html = readfile('viewer/index.html')
css = readfile('viewer/styles.css') css = readfile('viewer/styles.css')
js = readfile('viewer/scripts.js') js = readfile('viewer/scripts.js')
if doujinshi_obj is not None: if doujinshi_obj is not None:
title = doujinshi_obj.name serialize(doujinshi_obj, doujinshi_dir)
name = doujinshi_obj.name
if sys.version_info < (3, 0): if sys.version_info < (3, 0):
title = title.encode('utf-8') name = doujinshi_obj.name.encode('utf-8')
else: else:
title = 'nHentai HTML Viewer' name = {'title': 'nHentai HTML Viewer'}
data = html.format(TITLE=title, IMAGES=image_html, SCRIPTS=js, STYLES=css) data = html.format(TITLE=name, IMAGES=image_html, SCRIPTS=js, STYLES=css)
try: try:
if sys.version_info < (3, 0): if sys.version_info < (3, 0):
with open(os.path.join(doujinshi_dir, 'index.html'), 'w') as f: with open(os.path.join(doujinshi_dir, 'index.html'), 'w') as f:
@ -112,10 +113,12 @@ def generate_main_html(output_dir='./'):
Default output folder will be the CLI path. Default output folder will be the CLI path.
""" """
count = 0
image_html = '' image_html = ''
main = readfile('viewer/main.html') main = readfile('viewer/main.html')
css = readfile('viewer/main.css') css = readfile('viewer/main.css')
js = readfile('viewer/main.js')
element = '\n\ element = '\n\
<div class="gallery-favorite">\n\ <div class="gallery-favorite">\n\
<div class="gallery">\n\ <div class="gallery">\n\
@ -130,12 +133,10 @@ def generate_main_html(output_dir='./'):
doujinshi_dirs = next(os.walk('.'))[1] doujinshi_dirs = next(os.walk('.'))[1]
for folder in doujinshi_dirs: for folder in doujinshi_dirs:
files = os.listdir(folder) files = os.listdir(folder)
files.sort() files.sort()
if 'index.html' in files: if 'index.html' in files:
count += 1
logger.info('Add doujinshi \'{}\''.format(folder)) logger.info('Add doujinshi \'{}\''.format(folder))
else: else:
continue continue
@ -147,18 +148,19 @@ def generate_main_html(output_dir='./'):
title = 'nHentai HTML Viewer' title = 'nHentai HTML Viewer'
image_html += element.format(FOLDER=folder, IMAGE=image, TITLE=title) image_html += element.format(FOLDER=folder, IMAGE=image, TITLE=title)
if image_html == '': if image_html == '':
logger.warning('None index.html found, --gen-main paused.') logger.warning('None index.html found, --gen-main paused.')
return return
try: try:
data = main.format(STYLES=css, COUNT=count, PICTURE=image_html) data = main.format(STYLES=css, SCRIPTS=js, PICTURE=image_html)
if sys.version_info < (3, 0): if sys.version_info < (3, 0):
with open('./main.html', 'w') as f: with open('./main.html', 'w') as f:
f.write(data) f.write(data)
else: else:
with open('./main.html', 'wb') as f: with open('./main.html', 'wb') as f:
f.write(data.encode('utf-8')) f.write(data.encode('utf-8'))
shutil.copy(os.path.dirname(__file__)+'/viewer/logo.png', './')
set_js_database()
logger.log( logger.log(
15, 'Main Viewer has been write to \'{0}main.html\''.format(output_dir)) 15, 'Main Viewer has been write to \'{0}main.html\''.format(output_dir))
except Exception as e: except Exception as e:
@ -212,5 +214,3 @@ an invalid filename.
def signal_handler(signal, frame): def signal_handler(signal, frame):
logger.error('Ctrl-C signal received. Stopping...') logger.error('Ctrl-C signal received. Stopping...')
exit(1) exit(1)

View File

@ -2,6 +2,7 @@
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes, viewport-fit=cover" />
<title>{TITLE}</title> <title>{TITLE}</title>
<style> <style>
{STYLES} {STYLES}

BIN
nhentai/viewer/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -1,79 +1,14 @@
/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */ /*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */
/* Original from https://static.nhentai.net/css/main_style.9bb9b703e601.css */ /* Original from https://static.nhentai.net/css/main_style.9bb9b703e601.css */
html {
font-family: sans-serif;
line-height: 1.15;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%
}
body {
margin: 0
}
h1 {
font-size: 2em;
margin: .67em 0
}
a { a {
background-color: transparent; background-color: transparent;
-webkit-text-decoration-skip: objects -webkit-text-decoration-skip: objects
} }
a:active,a:hover {
outline-width: 0
}
abbr[title] {
border-bottom: none;
text-decoration: underline;
text-decoration: underline dotted
}
b,strong {
font-weight: inherit
}
b,strong {
font-weight: bolder
}
code,kbd,samp {
font-family: monospace,monospace;
font-size: 1em
}
small {
font-size: 80%
}
sub,sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline
}
sub {
bottom: -.25em
}
sup {
top: -.5em
}
img { img {
border-style: none border-style: none
} }
label {
display: block;
font-weight: 700;
text-align: justify;
white-space: nowrap
}
html { html {
box-sizing: border-box box-sizing: border-box
} }
@ -82,10 +17,6 @@ html {
box-sizing: inherit box-sizing: inherit
} }
h1,h2,h3,h4,h5,h6 {
font-weight: 700
}
body,html { body,html {
font-family: 'Noto Sans',sans-serif; font-family: 'Noto Sans',sans-serif;
font-size: 14px; font-size: 14px;
@ -104,25 +35,6 @@ a {
color: #34495e color: #34495e
} }
a:hover {
text-decoration: none;
color: #ed2553
}
a.count {
color: #999
}
a.bold {
font-weight: 700
}
code {
color: #ed2553;
border: 1px solid #fbd3dd;
background-color: #fef0f3
}
blockquote { blockquote {
border: 0 border: 0
} }
@ -130,15 +42,15 @@ blockquote {
.container { .container {
display: block; display: block;
clear: both; clear: both;
margin-left: auto; margin-left: 15rem;
margin-right: auto; margin-right: 0.5rem;
margin-bottom: 10px; margin-bottom: 5px;
margin-top: 10px; margin-top: 5px;
padding: 10px; padding: 4px;
border-radius: 9px; border-radius: 9px;
background-color: #ecf0f1; background-color: #ecf0f1;
width: 100%; width: 100% - 15rem;
max-width: 1200px max-width: 1500px
} }
.gallery,.gallery-favorite,.thumb-container { .gallery,.gallery-favorite,.thumb-container {
@ -156,7 +68,6 @@ blockquote {
.gallery,.gallery-favorite,.thumb-container { .gallery,.gallery-favorite,.thumb-container {
width:19%; width:19%;
margin: 3px; margin: 3px;
margin-bottom: 8px
} }
} }
@ -222,6 +133,186 @@ blockquote {
width: 100% width: 100%
} }
.sidenav {
height: 100%;
width: 15rem;
position: fixed;
z-index: 1;
top: 0;
left: 0;
background-color: #0d0d0d;
overflow: hidden;
padding-top: 20px;
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Old versions of Firefox */
ms-user-select: none; /* Internet Explorer/Edge */
user-select: none;
}
.sidenav a {
background-color: #eee;
padding: 5px 0px 5px 15px;
text-decoration: none;
font-size: 15px;
color: #0d0d0d9;
display: block;
text-align: left;
}
.sidenav img {
width:100%;
padding: 0px 5px 0px 5px;
}
.sidenav h1 {
font-size: 1.5em;
margin: 0px 0px 10px;
}
.sidenav a:hover {
color: white;
background-color: #EC2754;
}
.accordion {
font-weight: bold;
background-color: #eee;
color: #444;
padding: 10px 0px 5px 8px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
transition: 0.4s;
cursor:pointer;
}
.accordion:hover {
background-color: #ddd;
}
.accordion.active{
background-color:#ddd;
}
.nav-btn {
font-weight: bold;
background-color: #eee;
color: #444;
padding: 8px 8px 5px 9px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
}
.hidden {
display:none;
}
.nav-btn a{
font-weight: normal;
padding-right: 10px;
border-radius: 15px;
cursor: crosshair
}
.options {
display:block;
padding: 0px 0px 0px 0px;
background-color: #eee;
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
cursor:pointer;
}
.search{
background-color: #eee;
padding-right:40px;
white-space: nowrap;
padding-top: 5px;
height:43px;
}
.search input{
border-top-right-radius:10px;
padding-top:0;
padding-bottom:0;
font-size:1em;
width:100%;
height:38px;
vertical-align:top;
}
.btn{
border-top-left-radius:10px;
color:#fff;
font-size:100%;
padding: 8px;
width:38px;
background-color:#ed2553;
}
#tags{
text-align:left;
display: flex;
width:15rem;
justify-content: start;
margin: 2px 2px 2px 0px;
flex-wrap: wrap;
}
.btn-2{
font-weight:700;
padding-right:0.5rem;
padding-left:0.5rem;
color:#fff;
border:0;
font-size:100%;
height:1.25rem;
outline: 0;
border-radius: 0.3rem;
cursor: pointer;
margin:0.15rem;
transition: all 1s linear;
}
.btn-2#parody{
background-color: red;
}
.btn-2#character{
background-color: blue;
}
.btn-2#tag{
background-color: green;
}
.btn-2#artist{
background-color: fuchsia;
}
.btn-2#group{
background-color: teal;
}
.btn-2.hover{
filter: saturate(20%)
}
input,input:focus{
border:none;
outline:0;
}
html.theme-black,html.theme-black body { html.theme-black,html.theme-black body {
color: #d9d9d9; color: #d9d9d9;
background-color: #0d0d0d background-color: #0d0d0d
@ -231,14 +322,6 @@ html.theme-black #thumbnail-container,html.theme-black .container {
background-color: #1f1f1f background-color: #1f1f1f
} }
html.theme-black #thumbnail-container .lazyload,html.theme-black .lazyload {
background-color: #262626
}
html.theme-black #thumbnail-container .lazyload-loading,html.theme-black .lazyload-loading {
background-color: #2e2e2e
}
html.theme-black .gallery:hover .caption { html.theme-black .gallery:hover .caption {
box-shadow: 0 10px 20px rgba(0,0,0,.5) box-shadow: 0 10px 20px rgba(0,0,0,.5)
} }
@ -246,10 +329,4 @@ html.theme-black .gallery:hover .caption {
html.theme-black .caption { html.theme-black .caption {
background-color: #404040; background-color: #404040;
color: #d9d9d9 color: #d9d9d9
}
html.theme-black code {
color: #ed2553;
border: none;
background-color: #292929
} }

View File

@ -5,7 +5,8 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="theme-color" content="#1f1f1f" /> <meta name="theme-color" content="#1f1f1f" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes, viewport-fit=cover" /> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes, viewport-fit=cover" />
<title>nHentai &raquo; Viewer</title> <title>nHentai Viewer</title>
<script type="text/javascript" src="data.js"></script>
<!-- <link rel="stylesheet" href="./main.css"> --> <!-- <link rel="stylesheet" href="./main.css"> -->
<style> <style>
{STYLES} {STYLES}
@ -14,9 +15,27 @@
<body> <body>
<div id="content"> <div id="content">
<nav class="sidenav">
<h1>Main Folder({COUNT})</h1> <img src="logo.png">
<h1>nHentai Viewer</h1>
<button class="accordion">Language</button>
<div class="options" id="language">
<a>English</a>
<a>Japanese</a>
<a>Chinese</a>
</div>
<button class="accordion">Category</button>
<div class="options" id ="category">
<a>Doujinshi</a>
<a>Manga</a>
</div>
<button class="nav-btn hidden">Filters</button>
<div class="search">
<input autocomplete="off" type="search" id="tagfilter" name="q" value="" autocapitalize="none" required="">
<svg class="btn" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="white" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"/></svg>
<div id="tags">
</div>
</nav>
<div class="container" id="favcontainer"> <div class="container" id="favcontainer">
{PICTURE} {PICTURE}
@ -24,7 +43,9 @@
</div> <!-- container --> </div> <!-- container -->
</div> </div>
<script>
{SCRIPTS}
</script>
</body> </body>
</html> </html>

177
nhentai/viewer/main.js Normal file
View File

@ -0,0 +1,177 @@
//------------------------------------navbar script------------------------------------
var menu = document.getElementsByClassName("accordion");
for (var i = 0; i < menu.length; i++) {
menu[i].addEventListener("click", function() {
var panel = this.nextElementSibling;
if (panel.style.maxHeight) {
this.classList.toggle("active");
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
this.classList.toggle("active");
}
});
}
var language = document.getElementById("language").children;
for (var i = 0; i < language.length; i++){
language[i].addEventListener("click", function() {
toggler = document.getElementById("language")
toggler.style.maxHeight = null;
document.getElementsByClassName("accordion")[0].classList.toggle("active");
filter_maker(this.innerText, "language");
});
}
var category = document.getElementById("category").children;
for (var i = 0; i < category.length; i++){
category[i].addEventListener("click", function() {
document.getElementById("category").style.maxHeight = null;
document.getElementsByClassName("accordion")[1].classList.toggle("active");
filter_maker(this.innerText, "category");
});
}
//-----------------------------------------------------------------------------------
//----------------------------------Tags Script--------------------------------------
tag_maker(tags);
var tag = document.getElementsByClassName("btn-2");
for (var i = 0; i < tag.length; i++){
tag[i].addEventListener("click", function() {
filter_maker(this.innerText, this.id);
});
}
var input = document.getElementById("tagfilter");
input.addEventListener("input", function() {
var tags = document.querySelectorAll(".btn-2");
if (this.value.length > 0) {
for (var i = 0; i < tags.length; i++) {
var tag = tags[i];
var nome = tag.innerText;
var exp = new RegExp(this.value, "i");;
if (exp.test(nome)) {
tag.classList.remove("hidden");
}
else {
tag.classList.add("hidden");
}
}
} else {
for (var i = 0; i < tags.length; i++) {
var tag = tags[i];
tag.classList.add('hidden');
}
}
});
input.addEventListener('keypress', function (e) {
enter_search(e, this.value);
});
//-----------------------------------------------------------------------------------
//------------------------------------Functions--------------------------------------
function enter_search(e, input){
var count = 0;
var key = e.which || e.keyCode;
if (key === 13 && input.length > 0) {
var all_tags = document.getElementById("tags").children;
for(i = 0; i < all_tags.length; i++){
if (!all_tags[i].classList.contains("hidden")){
count++;
var tag_name = all_tags[i].innerText;
var tag_id = all_tags[i].id;
if (count>1){break}
}
}
if (count == 1){
filter_maker(tag_name, tag_id);
}
}
}
function filter_maker(text, class_value){
var check = filter_checker(text);
var nav_btn = document.getElementsByClassName("nav-btn")[0];
if (nav_btn.classList.contains("hidden")){
nav_btn.classList.toggle("hidden");
}
if (check == true){
var node = document.createElement("a");
var textnode = document.createTextNode(text);
node.appendChild(textnode);
node.classList.add(class_value);
nav_btn.appendChild(node);
filter_searcher();
}
}
function filter_searcher(){
var verifier = null;
var tags_filter = [];
var doujinshi_id = [];
var filter_tag = document.getElementsByClassName("nav-btn")[0].children;
filter_tag[filter_tag.length-1].addEventListener("click", function() {
this.remove();
try{
filter_searcher();
}
catch{
var gallery = document.getElementsByClassName("gallery-favorite");
for (var i = 0; i < gallery.length; i++){
gallery[i].classList.remove("hidden");
}
}
});
for (var i=0; i < filter_tag.length; i++){
var fclass = filter_tag[i].className;
var fname = filter_tag[i].innerText.toLowerCase();
tags_filter.push([fclass, fname])
}
for (var i=0; i < data.length; i++){
for (var j=0; j < tags_filter.length; j++){
try{
if(data[i][tags_filter[j][0]].includes(tags_filter[j][1])){
verifier = true;
}
else{
verifier = false;
break
}
}
catch{
verifier = false;
break
}
}
if (verifier){doujinshi_id.push(data[i].Folder);}
}
var gallery = document.getElementsByClassName("gallery-favorite");
for (var i = 0; i < gallery.length; i++){
gtext = gallery [i].children[0].children[0].children[1].innerText;
if(doujinshi_id.includes(gtext)){
gallery[i].classList.remove("hidden");
}
else{
gallery[i].classList.add("hidden");
}
}
}
function filter_checker(text){
var filter_tags = document.getElementsByClassName("nav-btn")[0].children;
if (filter_tags == null){return true;}
for (var i=0; i < filter_tags.length; i++){
if (filter_tags[i].innerText == text){return false;}
}
return true;
}
function tag_maker(data){
for (i in data){
for (j in data[i]){
var node = document.createElement("button");
var textnode = document.createTextNode(data[i][j]);
node.appendChild(textnode);
node.classList.add("btn-2");
node.setAttribute('id', i);
node.classList.add("hidden");
document.getElementById("tags").appendChild(node);
}
}
}

View File

@ -1,85 +1,85 @@
const pages = Array.from(document.querySelectorAll('img.image-item')); const pages = Array.from(document.querySelectorAll('img.image-item'));
let currentPage = 0; let currentPage = 0;
function changePage(pageNum) { function changePage(pageNum) {
const previous = pages[currentPage]; const previous = pages[currentPage];
const current = pages[pageNum]; const current = pages[pageNum];
if (current == null) { if (current == null) {
return; return;
} }
previous.classList.remove('current'); previous.classList.remove('current');
current.classList.add('current'); current.classList.add('current');
currentPage = pageNum; currentPage = pageNum;
const display = document.getElementById('dest'); const display = document.getElementById('dest');
display.style.backgroundImage = `url("${current.src}")`; display.style.backgroundImage = `url("${current.src}")`;
scroll(0,0) scroll(0,0)
document.getElementById('page-num') document.getElementById('page-num')
.innerText = [ .innerText = [
(pageNum + 1).toLocaleString(), (pageNum + 1).toLocaleString(),
pages.length.toLocaleString() pages.length.toLocaleString()
].join('\u200a/\u200a'); ].join('\u200a/\u200a');
} }
changePage(0); changePage(0);
document.getElementById('list').onclick = event => { document.getElementById('list').onclick = event => {
if (pages.includes(event.target)) { if (pages.includes(event.target)) {
changePage(pages.indexOf(event.target)); changePage(pages.indexOf(event.target));
} }
}; };
document.getElementById('image-container').onclick = event => { document.getElementById('image-container').onclick = event => {
const width = document.getElementById('image-container').clientWidth; const width = document.getElementById('image-container').clientWidth;
const clickPos = event.clientX / width; const clickPos = event.clientX / width;
if (clickPos < 0.5) { if (clickPos < 0.5) {
changePage(currentPage - 1); changePage(currentPage - 1);
} else { } else {
changePage(currentPage + 1); changePage(currentPage + 1);
} }
}; };
document.onkeypress = event => { document.onkeypress = event => {
switch (event.key.toLowerCase()) { switch (event.key.toLowerCase()) {
// Previous Image // Previous Image
case 'w': case 'w':
scrollBy(0, -40); scrollBy(0, -40);
break; break;
case 'a': case 'a':
changePage(currentPage - 1); changePage(currentPage - 1);
break; break;
// Return to previous page // Return to previous page
case 'q': case 'q':
window.history.go(-1); window.history.go(-1);
break; break;
// Next Image // Next Image
case ' ': case ' ':
case 's': case 's':
scrollBy(0, 40); scrollBy(0, 40);
break; break;
case 'd': case 'd':
changePage(currentPage + 1); changePage(currentPage + 1);
break; break;
}// remove arrow cause it won't work }// remove arrow cause it won't work
}; };
document.onkeydown = event =>{ document.onkeydown = event =>{
switch (event.keyCode) { switch (event.keyCode) {
case 37: //left case 37: //left
changePage(currentPage - 1); changePage(currentPage - 1);
break; break;
case 38: //up case 38: //up
break; break;
case 39: //right case 39: //right
changePage(currentPage + 1); changePage(currentPage + 1);
break; break;
case 40: //down case 40: //down
break; break;
} }
}; };

View File

@ -1,69 +1,70 @@
*, *::after, *::before {
box-sizing: border-box; *, *::after, *::before {
} box-sizing: border-box;
}
img {
vertical-align: middle; img {
} vertical-align: middle;
}
html, body {
display: flex; html, body {
background-color: #e8e6e6; display: flex;
height: 100%; background-color: #e8e6e6;
width: 100%; height: 100%;
padding: 0; width: 100%;
margin: 0; padding: 0;
font-family: sans-serif; margin: 0;
} font-family: sans-serif;
}
#list {
height: 2000px; #list {
overflow: scroll; height: 2000px;
width: 260px; overflow: scroll;
text-align: center; width: 260px;
} text-align: center;
}
#list img {
width: 200px; #list img {
padding: 10px; width: 200px;
border-radius: 10px; padding: 10px;
margin: 15px 0; border-radius: 10px;
cursor: pointer; margin: 15px 0;
} cursor: pointer;
}
#list img.current {
background: #0003; #list img.current {
} background: #0003;
}
#image-container {
flex: auto; #image-container {
height: 2000px; flex: auto;
background: #222; height: 2000px;
color: #fff; background: #222;
text-align: center; color: #fff;
cursor: pointer; text-align: center;
-webkit-user-select: none; cursor: pointer;
user-select: none; -webkit-user-select: none;
position: relative; user-select: none;
} position: relative;
}
#image-container #dest {
height: 2000px; #image-container #dest {
width: 100%; height: 2000px;
background-size: contain; width: 100%;
background-repeat: no-repeat; background-size: contain;
background-position: top; background-repeat: no-repeat;
} background-position: top;
}
#image-container #page-num {
position: static; #image-container #page-num {
font-size: 14pt; position: static;
left: 10px; font-size: 14pt;
bottom: 5px; left: 10px;
font-weight: bold; bottom: 5px;
opacity: 0.75; font-weight: bold;
text-shadow: /* Duplicate the same shadow to make it very strong */ opacity: 0.75;
0 0 2px #222, text-shadow: /* Duplicate the same shadow to make it very strong */
0 0 2px #222, 0 0 2px #222,
0 0 2px #222; 0 0 2px #222,
} 0 0 2px #222;
}