From 8e8f935a9be77947930e596dd78ec1026b274925 Mon Sep 17 00:00:00 2001 From: symant233 Date: Sun, 5 May 2019 19:44:08 +0800 Subject: [PATCH 01/21] set alias for local:1080 proxy --- nhentai/cmdline.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/nhentai/cmdline.py b/nhentai/cmdline.py index 533b3c8..d8ab7bf 100644 --- a/nhentai/cmdline.py +++ b/nhentai/cmdline.py @@ -57,7 +57,7 @@ def cmd_parser(): parser.add_option('--timeout', type='int', dest='timeout', action='store', default=30, help='timeout for downloading doujinshi') parser.add_option('--proxy', type='string', dest='proxy', action='store', default='', - help='uses a proxy, for example: http://127.0.0.1:1080') + help='uses a proxy, for example: --proxy "http://127.0.0.1:1080" or its alias "default"') parser.add_option('--html', dest='html_viewer', action='store_true', help='generate a html viewer at current directory') @@ -124,7 +124,12 @@ def cmd_parser(): if args.proxy: proxy_url = urlparse(args.proxy) - if proxy_url.scheme not in ('http', 'https'): + if args.proxy == 'default' or 'd': + constant.PROXY = { + 'http': "http://127.0.0.1:1080", + 'https': "http://127.0.0.1:1080" + } + elif 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} From 18bdab19620affce29e53d4ceb659e77b1c7933b Mon Sep 17 00:00:00 2001 From: symant233 Date: Sun, 5 May 2019 19:44:44 +0800 Subject: [PATCH 02/21] add main viewer --- nhentai/cmdline.py | 8 +- nhentai/utils.py | 6 + nhentai/viewer/main.css | 1893 ++++++++++++++++++++++++++++++++++++++ nhentai/viewer/main.html | 37 + 4 files changed, 1943 insertions(+), 1 deletion(-) create mode 100644 nhentai/viewer/main.css create mode 100644 nhentai/viewer/main.html diff --git a/nhentai/cmdline.py b/nhentai/cmdline.py index d8ab7bf..7e199b4 100644 --- a/nhentai/cmdline.py +++ b/nhentai/cmdline.py @@ -9,7 +9,7 @@ except ImportError: pass import nhentai.constant as constant -from nhentai.utils import urlparse, generate_html +from nhentai.utils import urlparse, generate_html, generate_main_html from nhentai.logger import logger try: @@ -60,6 +60,8 @@ def cmd_parser(): help='uses a proxy, for example: --proxy "http://127.0.0.1:1080" or its alias "default"') parser.add_option('--html', dest='html_viewer', action='store_true', help='generate a html viewer at current directory') + parser.add_option('--gen-main', dest='main_viewer', action='store_true', + help='generate a main viewer contain all the doujin in the folder') parser.add_option('--login', '-l', type='str', dest='login', action='store', help='username:password pair of nhentai account') @@ -86,6 +88,10 @@ def cmd_parser(): generate_html() exit(0) + if args.main_viewer: + generate_main_html() + exit(0) + if args.login: try: _, _ = args.login.split(':', 1) diff --git a/nhentai/utils.py b/nhentai/utils.py index f884d2f..4239e91 100644 --- a/nhentai/utils.py +++ b/nhentai/utils.py @@ -81,6 +81,12 @@ def generate_html(output_dir='.', doujinshi_obj=None): except Exception as e: logger.warning('Writen HTML Viewer failed ({})'.format(str(e))) +def generate_main_html(output_dir='.'): + """Generete a main html to show all the contain doujinshi. + With a link to thier `index.html`. + Default output folder will be the CLI path. + """ + pass def generate_cbz(output_dir='.', doujinshi_obj=None, rm_origin_dir=False): if doujinshi_obj is not None: diff --git a/nhentai/viewer/main.css b/nhentai/viewer/main.css new file mode 100644 index 0000000..3ec6c77 --- /dev/null +++ b/nhentai/viewer/main.css @@ -0,0 +1,1893 @@ +/* +Original from https://static.nhentai.net/css/main_style.9bb9b703e601.css +This `.css` file need to delete some unused elements for our Viewer. +Please fork the project to make contributions. +*/ +html { + font-family: sans-serif; + line-height: 1.15; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100% +} + +body { + margin: 0 +} + +article,aside,footer,header,nav,section { + display: block +} + +h1 { + font-size: 2em; + margin: .67em 0 +} + +figcaption,figure,main { + display: block +} + +figure { + margin: 1em 40px +} + +hr { + box-sizing: content-box; + height: 0; + overflow: visible +} + +pre { + font-family: monospace,monospace; + font-size: 1em +} + +a { + background-color: transparent; + -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 +} + +dfn { + font-style: italic +} + +mark { + background-color: #ff0; + color: #000 +} + +small { + font-size: 80% +} + +sub,sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline +} + +sub { + bottom: -.25em +} + +sup { + top: -.5em +} + +img { + border-style: none +} + +svg:not(:root) { + overflow: hidden +} + + +@media (min-width: 1201px) { + nav form { + margin-right:10px; + width: 25% + } + + nav #hamburger,nav .menu .dropdown { + display: none + } +} + +@media (min-width: 601px) and (max-width:1200px) { + nav form { + width:35% + } + + nav .menu .desktop { + display: none + } + + nav .menu .dropdown-menu { + display: none + } + + nav #hamburger { + display: none + } + + nav .dropdown { + position: relative + } + + nav .dropdown .dropdown-menu.open { + display: inline-block; + position: absolute; + left: 0; + top: 100%; + border-radius: 3px; + padding-left: 0; + text-align: left; + background-color: #2e3c4a; + z-index: 3; + box-shadow: 0 0 30px rgba(0,0,0,.5) + } + + nav .dropdown .dropdown-menu.open:before { + position: absolute; + content: ''; + pointer-events: none; + bottom: 100%; + left: 15px; + border-color: transparent transparent #2e3c4a transparent; + border-style: solid; + border-width: 10px 10px 10px 10px; + height: 0; + width: 0 + } + + nav .dropdown .dropdown-menu.open li { + width: 100% + } + + nav .dropdown .dropdown-menu.open li a { + line-height: 2; + height: auto; + width: 100% + } + + nav .dropdown .dropdown-menu.open li:first-of-type a { + border-top-right-radius: 3px; + border-top-left-radius: 3px + } + + nav .dropdown .dropdown-menu.open li:last-of-type a { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px + } +} + +@media (max-width: 600px) { + nav { + padding-right:50px + } + + nav form { + width: 100% + } + + nav .collapse { + display: block; + width: 100%; + overflow: hidden; + max-height: 0; + transition: max-height .5s + } + + nav .collapse.open { + max-height: 370px + } + + nav .collapse .menu.left,nav .collapse .menu.right { + float: none + } + + nav .collapse .menu .dropdown { + display: none + } + + nav .collapse .menu li { + display: block + } + + nav .collapse .menu li a { + width: 100%; + height: 35px; + line-height: 35px + } +} + +#settings-container { + text-align: left +} + +#settings-container input[type=email],#settings-container input[type=password],#settings-container input[type=text] { + width: 100%; + height: 30px; + padding-left: 10px; + border-radius: 3px +} + +#settings-container h2 { + text-align: center +} + +#settings-container form { + max-width: 800px; + margin-right: auto; + margin-left: auto +} + +#user-container { + overflow: auto +} + +@media (min-width: 600px) { + .user-info { + text-align:left; + float: left + } + + .bigavatar { + float: left; + margin: 2em + } +} + +.form-group { + margin: 20px +} + +label { + display: block; + font-weight: 700; + text-align: justify; + white-space: nowrap +} + +html { + box-sizing: border-box +} + +*,:after,:before { + box-sizing: inherit +} + +h1,h2,h3,h4,h5,h6 { + font-weight: 700 +} + +body,html { + font-family: 'Noto Sans',sans-serif; + font-size: 14px; + line-height: 1.42857143; + height: 100%; + margin: 0; + text-align: center; + color: #34495e; + background-color: #fff; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale +} + +button,input,textarea { + font-family: 'Noto Sans',sans-serif +} + +a { + text-decoration: none; + 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 +} + +textarea { + resize: none +} + +blockquote { + border: 0 +} + +form.inline { + display: inline +} + +.btn { + display: inline-block; + vertical-align: middle; + font-weight: 700; + cursor: pointer; + padding: 0; + padding-right: 12px; + padding-left: 12px; + color: #fff; + border: 0; + border-radius: 3px; + outline: 0; + font-size: 100%; + height: 40px; + line-height: 40px; + margin: 3px; + -webkit-font-smoothing: antialiased +} + +.btn:hover { + color: #fff; + background: #f15478 +} + +.btn.btn-unstyled { + font-weight: 400; + line-height: 1; + height: auto; + color: inherit; + padding: 0; + margin: 0; + background: 0 0; + cursor: pointer; + border: none +} + +.btn.btn-thin { + font-weight: 400; + height: auto; + line-height: 1; + margin-top: 5px; + margin-bottom: 10px; + padding-top: 5px; + padding-right: 6px; + padding-bottom: 5px; + padding-left: 6px +} + +.btn.btn-primary { + background-color: #ed2553 +} + +.btn.btn-primary:active,.btn.btn-primary:focus,.btn.btn-primary:hover { + color: #fff; + background: #f15478 +} + +.btn.btn-primary.disabled,.btn.btn-primary:disabled { + cursor: default; + background-color: #be546c +} + +.btn.btn-secondary { + background-color: #475d73 +} + +.btn.btn-secondary:active,.btn.btn-secondary:focus,.btn.btn-secondary:hover { + background: #516a83 +} + +.btn.btn-secondary.disabled,.btn.btn-secondary:disabled { + cursor: default; + background-color: #555d65 +} + +.btn.btn-square { + width: 40px; + height: 40px; + margin: 5px; + vertical-align: middle +} + +.edit { + font-size: 15px; + padding: 5px; + padding-top: 2px; + padding-bottom: 2px; + vertical-align: middle; + color: #fff; + border-radius: 3px; + background: #2a3744 +} + +.edit:hover { + color: #fff; + background: #3d5064 +} + +.container { + display: block; + clear: both; + margin-left: auto; + margin-right: auto; + margin-bottom: 10px; + margin-top: 10px; + padding: 10px; + border-radius: 9px; + background-color: #ecf0f1; + width: 100%; + max-width: 1200px +} + +@media screen and (orientation: landscape) { + .container { + padding-left:calc(5px + constant(safe-area-inset-left)); + padding-right: calc(5px + constant(safe-area-inset-right)) + } +} + +.container.left { + text-align: left +} + +.container.right { + text-align: right +} + +@media screen and (max-width: 1200px) { + .container { + border-radius:0 + } +} + +.container.advertisement.advertisement { + overflow: hidden; + margin-right: auto; + margin-left: auto; + text-align: center; + background-color: inherit; + min-height: 90px +} + +.container.advertisement.advertisement iframe { + display: block; + margin-right: auto; + margin-left: auto +} + +.container:last-of-type { + margin-bottom: 0 +} + +.invisible { + z-index: -1; + visibility: hidden +} + +.pagination { + font-size: 1.3em; + margin-bottom: 2em; + margin-top: 2em +} + +.pagination .first,.pagination .last,.pagination .next,.pagination .page,.pagination .previous { + display: inline-block; + min-width: 35px; + padding: 5px +} + +.pagination .first.current,.pagination .last.current,.pagination .next.current,.pagination .page.current,.pagination .previous.current { + font-weight: 700; + border-radius: 100%; + background-color: #f2f5f5 +} + +.pagination .first:hover,.pagination .last:hover,.pagination .next:hover,.pagination .page:hover,.pagination .previous:hover { + border-radius: 100%; + background-color: #f8f9fa +} + +.alphabetical-pagination { + margin-bottom: 0; + padding-left: 0; + list-style: none +} + +.alphabetical-pagination li { + display: inline-block +} + +.alphabetical-pagination li a,.alphabetical-pagination li span { + font-size: 1.15em; + display: inline-block; + min-width: 26px; + padding: 2px; + text-align: center +} + +.alphabetical-pagination li a:hover,.alphabetical-pagination li span:hover { + border-radius: 100%; + background-color: #f5f7f7 +} + +.alphabetical-pagination li a.current,.alphabetical-pagination li span.current { + border-radius: 100%; + font-weight: 700 +} + +.alphabetical-pagination li a.disabled,.alphabetical-pagination li span.disabled { + color: #999 +} + +.tag { + display: inline-block; + border-radius: 3px; + color: #fff; + margin: 2px; + padding: 2px 6px 3px; + background: #364657 +} + +.tag:hover { + color: #fff; + background: #556f89 +} + +.tag .count { + font-weight: 400; + color: #a5b6c8 +} + +#footer-container { + margin-top: 15px; + margin-bottom: 6px; + background-color: inherit +} + +#footer-container .footer-item a { + margin: 10px; + color: #5e7980 +} + +.thumb-container { + text-align: center +} + +.thumb-container img { + margin-top: 2px; + margin-right: 2px; + margin-left: 2px +} + +.blacklisted-tag { + text-decoration: line-through +} + +.blacklisted.gallery { + opacity: .1; + -webkit-filter: blur(5px); + filter: blur(5px) +} + +.blacklisted.gallery:hover { + opacity: .2; + -webkit-filter: blur(0); + filter: blur(0) +} + +.blacklisted.tag { + opacity: .5; + text-decoration: line-through +} + +.gallery,.gallery-favorite,.thumb-container { + display: inline-block; + vertical-align: top +} + +.gallery img,.gallery-favorite img,.thumb-container img { + display: block; + max-width: 100%; + height: auto +} + +@media screen and (min-width: 980px) { + .gallery,.gallery-favorite,.thumb-container { + width:19%; + margin: 3px; + margin-bottom: 8px + } +} + +@media screen and (max-width: 979px) { + .gallery,.gallery-favorite,.thumb-container { + width:24%; + margin: 2px + } +} + +@media screen and (max-width: 772px) { + .gallery,.gallery-favorite,.thumb-container { + width:32%; + margin: 1.5px + } +} + +@media screen and (max-width: 500px) { + .gallery,.gallery-favorite,.thumb-container { + width:49%; + margin: .5px + } +} + +.gallery a,.gallery-favorite a { + display: block +} + +.gallery a img,.gallery-favorite a img { + position: absolute +} + +.caption { + line-height: 15px; + left: 0; + right: 0; + top: 100%; + position: absolute; + z-index: 10; + overflow: hidden; + width: 100%; + max-height: 34px; + padding: 3px; + background-color: #fff; + font-weight: 700; + display: block; + text-align: center; + text-decoration: none; + color: #34495e +} + +.gallery { + position: relative; + margin-bottom: 3em +} + +.gallery:hover .caption { + max-height: 100%; + box-shadow: 0 10px 20px rgba(100,100,100,.5) +} + +.gallery-favorite .btn { + margin-top: 20px +} + +.gallery-favorite .gallery { + width: 100% +} + +.gallery[data-tags*="12227"] .caption:before,.gallery[data-tags*="29963"] .caption:before,.gallery[data-tags*="6346"] .caption:before { + content: ""; + display: inline-block; + vertical-align: middle; + z-index: 2; + background-size: contain; + width: 18px; + height: 12px +} + +.gallery[data-tags*="6346"] .caption:before { + background-image: url(data:image/gif;base64,R0lGODlhSAAwAOZhAO1UZPn6/PX+//X8//z9/vX4+/X//////+1TY+1PX/X7/vX7/fb///X9/+1SYvf4++1QYOxMXPn7/P7+//Xw9PXx9fGjre5pd/X8/vb4+/GnsPPN1O1OX/CQm+95hvTd4vXy9vGXovTb4PCVoO1TZPPS1+1OXu1QYfPT2fTm6/Ggqu1dbfX5/O5icPCSnfCTne1RYvTq7u5hcfGkrvTv8+5jce1UZfTr7/To7fKqsvPJz+1WZvPO0/CTnvXt8fPP1e93g/K0vPLEy/LEzO1PYO1YaO+Aje5od+1ZaPLDy+5gb/LFzO5ice+Klu1XZ++Cju1YZ+5hcPXs8PGirOxKW++JlexLXO+CjfK2v/K2vfXs8e+BjPG0u+5hb+1VZfb4+vX3+v///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAGEALAAAAABIADAAAAf/gGFhBwQBD1+IiYqLjI2Oj5CLDwEEB4JhEwFfYJydnp+goaKjpKNfARODEqWsra6vnRKEm7C1trBfhbe7vKQBGb3BwmCHw8a3iMfKr8nLzqTNz9Kf0dPW1dbS2NnO28MFCw0CBgINCwXdtMcFAz5LFi9NLxZCWgMsyt68GCBYTxdIIFhJAOWCkSAVBhzTdwsDjhBMIiRwAMALAAcJInQZkULhMIa1GtzoAYCIl5MoUUIg0SGGgI/qeimokGMHgpQ4T97UQEGBMJCvBJSoASOnUQcteAhA1wtoqwUUZnCwYdSoCRU0fDaNucvABw83q+ZEAESEgWBOWRlAIaOiWJwA0Fb8YICW6y0GGyy+zXlCB92twvDq3Zuy719eaUutbUsYZdy5dYV5BdtYZ9mzgINBlUq18dWskYUJJdoYqVKmiO3emlkzrNidPX+qviWSJASxK1u+lH3MYYgoEilaxBhBCUePvI8NAJHlyoUiCagkcHJkC5eEC2fzYudjiAUXVVxMSSLlXj7tvcCJI2cOdXZu19DDDz0/Xf1nie/jkq/fVv7+rXwBDIDGZBAAgcZQwh+CpeRywCoM8iILJppEWMspqQhCiCGRdOjhh5BMUokggQAAOw==) +} + +.gallery[data-tags*="12227"] .caption:before { + background-image: url(data:image/gif;base64,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) +} + +.gallery[data-tags*="29963"] .caption:before { + background-image: url(data:image/gif;base64,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) +} + +.gallery-edit { + text-align: left; + overflow: auto +} + +.gallery-edit .gallery { + float: left +} + +.gallery-edit .tag-container { + display: inline-block +} + +#info-container { + height: auto; + padding: 40px; + padding-top: 10px; + padding-bottom: 20px; + text-align: left +} + +#info-container section.text { + padding-top: 10px; + padding-bottom: 10px +} + +#info-container section:not(:last-of-type) { + padding-bottom: 10px; + border-bottom: 1px solid #dde4e6 +} + +#info-container h1 { + font-size: 2em +} + +#info-container h2 { + font-size: 1.6em +} + +#info-container h3 { + font-size: 1.4em +} + +#info-container li { + margin: 5px +} + +#info-container a { + color: #ed2553 +} + +#info-container #thanks { + margin-top: 20px; + text-align: center +} + +.sort { + margin-top: -15px +} + +.sort:before { + content: ""; + display: block; + clear: both; + margin-top: 1em +} + +.sort a { + font-size: 20px; + display: inline-block; + padding: 20px +} + +.sort a.active { + font-weight: 700 +} + +.sort+.container { + margin-top: 0 +} + +#info { + height: auto; + padding: 10px; + text-align: left +} + +#info h1 { + font-size: 20px +} + +#info h2 { + font-size: 17px; + margin-top: 10px; + margin-bottom: 20px +} + +#info .field-name { + font-weight: 700; + margin-top: 5px; + margin-bottom: 5px +} + +#info .buttons { + margin-top: 30px +} + +#info .buttons .btn { + min-width: 120px; + text-align: center; + margin-left: 0; + margin-right: 5px; + margin-bottom: 10px +} + +#info a { + font-weight: 700 +} + +#profile-update input[type=file]#avatar { + display: none +} + +#profile-update #avatar-upload { + overflow: auto; + width: 200px; + margin-bottom: 10px +} + +#profile-update #avatar-upload img { + margin-bottom: 10px +} + +#profile-update #avatar-upload .btn { + width: 110px +} + +#profile-update #avatar-upload label[for=avatar] { + float: left; + width: 70px; + padding-right: 5px; + padding-left: 5px +} + +#profile-update #avatar-upload label[for=avatar-clear] { + float: right +} + +#profile-update #avatar-upload #avatar-clear { + position: relative; + top: 2px; + margin: 0; + margin-left: 3px +} + +#bigcontainer { + margin-bottom: 25px; + padding: 20px; + padding-top: 30px; + padding-bottom: 30px +} + +#bigcontainer form { + display: inline-block +} + +@media screen and (min-width: 768px) { + #bigcontainer #cover,#bigcontainer #info-block { + display:inline-block; + width: 48%; + vertical-align: top + } +} + +@media screen and (max-width: 500px) { + #bigcontainer #cover,#bigcontainer #info-block { + width:100%; + margin: .5px + } +} + +#bigcontainer #cover img,#bigcontainer #info-block img { + max-width: 100% +} + +.fa-heart,.fa-heart-o { + margin: 2px +} + +.lazyload { + background-color: #e3e9eb +} + +.lazyload-loading { + background: #dae2e4 +} + +#thumbnail-container { + margin-bottom: 25px; + padding: 15px; + text-align: left; + background-color: #2a3744 +} + +@media screen and (max-width: 980px) { + #thumbnail-container { + padding:5px; + padding-top: 10px; + border-radius: 0 + } +} + +#thumbnail-container .lazyload { + background-color: #303f4d +} + +#thumbnail-container .lazyload-loading { + background-color: #364657 +} + +#thumbnail-container .gallerythumb { + display: inline-block; + margin-bottom: 3px; + vertical-align: middle +} + +#thumbnail-container .gallerythumb a { + display: inline-block +} + +#related-container { + margin-bottom: 25px; + padding-bottom: 20px +} + +.commentform { + max-width: 100% +} + +.login-comment { + text-decoration: underline +} + +#comment-container { + padding: 20px +} + +#comment-container #id_body { + width: 85%; + height: 90px; + margin-bottom: 5px; + padding: 10px; + border: 0; + border-radius: 3px; + outline: 0; + -webkit-appearance: none +} + +.commentbutton { + width: 120px; + height: 40px; + color: #fff; + border: 0; + border-radius: 3px +} + +.comment { + display: flex; + text-align: left; + margin-bottom: 10px; + padding: 8px +} + +.comment .header { + display: flex; + flex-direction: row; + margin-bottom: 5px +} + +.comment .header time { + margin-left: .5em; + opacity: .6 +} + +.comment .header .right { + margin-left: auto; + text-align: right +} + +.comment .header .right .comment-delete { + opacity: .6 +} + +.comment .header .right .comment-delete:hover { + opacity: .9 +} + +.comment .body-wrapper { + flex: 1 +} + +.comment .body-wrapper .body { + word-break: break-word +} + +.comment .body-wrapper .body a { + color: #ed2553 +} + +.comment .body-wrapper .body a:hover { + color: #ef3d66; + text-decoration: underline +} + +.comment:target { + background-color: #d7e0e2 +} + +#favorites-search-bar { + width: 200px; + height: 35px; + padding: 10px; + border: 0; + border-radius: 3px; + outline: 0; + outline-width: 0; + outline-style: none; + background-color: #dae2e4; + -webkit-appearance: none +} + +#favorites-search-form { + margin-bottom: 20px +} + +#favorites-search { + display: inline-block +} + +#favorites-search input { + background-color: #ecf0f1 +} + +#favorites-search-button { + width: 40px; + height: 35px +} + +#favorites-random-button { + margin-left: -15px +} + +.remove-button { + margin-top: 5px; + margin-bottom: 10px; + background-color: #e32636 +} + +.remove-button:hover { + text-decoration: none; + background-color: #e6414f +} + +#favcontainer { + padding: 10px; + padding-bottom: 40px +} + +.removed { + opacity: .5 +} + +#tag-container { + padding-bottom: 5px; + -webkit-columns: 200px; + -moz-columns: 200px; + -ms-columns: 200px; + -o-columns: 200px; + columns: 200px; + -webkit-column-rule-color: #e2e8e9; + -webkit-column-rule-width: 1px; + -webkit-column-rule-style: solid; + -moz-column-rule-color: #e2e8e9; + -moz-column-rule-width: 1px; + -moz-column-rule-style: solid; + -ms-column-rule-color: #e2e8e9; + -ms-column-rule-width: 1px; + -ms-column-rule-style: solid; + -o-column-rule-color: #e2e8e9; + -o-column-rule-width: 1px; + -o-column-rule-style: solid; + column-rule-color: #e2e8e9; + column-rule-width: 1px; + column-rule-style: solid +} + +#tag-container .tag { + display: block; + padding-top: 5px; + padding-bottom: 5px; + border-radius: 5px; + -webkit-column-break-inside: avoid; + -moz-column-break-inside: avoid; + page-break-inside: avoid; + background-color: transparent; + color: #34495e +} + +#tag-container .tag:hover { + background-color: #f5f7f7 +} + +#tag-container section { + border: 1px solid transparent; + border-radius: 5px +} + +#tag-container section:first-of-type h2 { + margin-top: 0 +} + +#tag-container section:target { + border-color: #cfd9db; + background-color: #f5f7f7 +} + +#tag-container section:target .tag:hover { + background-color: #fff +} + +input,input:focus { + -webkit-transition: none; + -moz-transition: none; + transition: none; + border: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + outline: 0 +} + +div.form-control { + background-color: transparent +} + +.container.error h1 { + font-size: 50px +} + +.container.error blockquote { + font-size: 25px; + font-style: italic +} + +.container.error img { + max-width: 100% +} + +.nobold { + font-weight: 400 +} + +.hidden { + display: none +} + +#favorites-search { + padding-left: 20px; + padding-right: 60px; + margin-left: auto; + margin-right: auto; + max-width: 300px +} + +.avatar { + display: inline-block; + position: relative +} + +.avatar img { + display: inline-block; + vertical-align: middle; + width: 50px; + height: 50px; + margin-right: 10px +} + +.avatar .username { + display: inline-block; + vertical-align: middle +} + +.edit-queue { + width: 100% +} + +.edit-queue .user img { + vertical-align: middle; + margin-right: 5px +} + +.edit-queue tr td:nth-child(2) { + width: 20% +} + +.edit-queue tr td:nth-child(6) { + min-width: 160px +} + +.edit-queue tr.voted { + display: none +} + +.g-recaptcha { + display: inline-block +} + +.tag-autocomplete { + display: inline-block +} + +.tag-autocomplete .tag-wrapper { + display: inline-block +} + +.tag-autocomplete .autocomplete-wrapper { + position: relative; + display: inline-block +} + +.tag-autocomplete .autocomplete-wrapper.hidden { + display: none +} + +.tag-autocomplete .autocomplete-wrapper .tag-input { + font-weight: 400; + margin: 0; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0 +} + +.tag-autocomplete .autocomplete-wrapper .dropdown { + z-index: 2; + position: absolute; + left: 0; + width: auto; + right: 0; + background-color: #364657; + margin-top: 0; + list-style-type: none; + padding-left: 0; + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; + box-shadow: 0 10px 20px rgba(0,0,0,.5) +} + +.tag-autocomplete .autocomplete-wrapper .dropdown li.active>a.tag { + background: #506984!important +} + +.tag-autocomplete .autocomplete-wrapper .dropdown li.disabled { + font-style: italic; + padding: 2px 6px 3px; + color: gray +} + +.tag-container .name { + font-weight: 700 +} + +.tag-container .tag.tag-deleted { + background-color: #643d3d; + text-decoration: line-through +} + +.tag-container .tag.tag-added { + background-color: #3d643f +} + +.tag-container .tag.tag-created { + background-color: #7c7b36 +} + +.tag-container .tag.tag-created .count { + color: #b3b3b3 +} + +.tag-container .tag.tag-new { + cursor: pointer +} + +.tag.tag-input { + margin-bottom: 0 +} + +#messages { + position: fixed; + top: 0; + width: 100%; + z-index: 20 +} + +#messages .alert { + display: flex; + padding: 15px; + margin-top: 10px; + border: 1px solid transparent; + border-radius: 4px +} + +#messages .alert.alert-info,#messages .alert.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6 +} + +#messages .alert.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1 +} + +#messages .alert.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc +} + +#messages .alert .alert-close { + margin-left: auto; + color: #34495e +} + +.modal-wrapper { + display: block; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + padding: 10px; + background: rgba(0,0,0,.6) +} + +.modal-wrapper.modal-compact .modal-inner { + display: inline-block +} + +.modal-wrapper.modal-compact .modal-inner h1 { + display: none +} + +.modal-wrapper .modal-inner { + overflow: auto; + background-color: #fff; + border-radius: 5px; + max-width: 600px; + padding-top: 10px; + margin-left: auto; + margin-right: auto; + margin-top: 100px; + box-shadow: 0 0 60px rgba(0,0,0,.5) +} + +.modal-wrapper .modal-inner .contents { + padding: 10px +} + +.modal-wrapper .modal-inner .buttons { + padding: 5px; + margin-top: 10px; + background-color: #e6e6e6 +} + +#chatbanner { + margin: 20px +} + +#chatbanner img { + border: 2px solid #ed2553 +} + +.announcement { + margin-top: 20px; + margin-bottom: 20px; + margin-left: 5px; + margin-right: 5px; + font-size: 18px +} + +.announcement a[href] { + color: #ed2553 +} + +#errors { + text-align: center; + list-style-type: none; + padding-left: 0; + color: #ef808a +} + +select { + font-weight: 400 +} + +#page-container { + margin-bottom: 5px +} + +@media (max-width: 768px) { + #page-container img { + width:100%; + height: auto + } +} + +@media (min-width: 769px) { + #page-container img { + max-width:90%; + height: auto + } +} + +#page-container .pagination { + margin-bottom: 5px; + margin-top: 5px +} + +#page-container .pagination .first,#page-container .pagination .last,#page-container .pagination .next,#page-container .pagination .page,#page-container .pagination .previous { + font-size: 17px; + margin-bottom: 10px; + margin-top: 10px +} + +@media (min-width: 397px) { + #page-container .pagination .first,#page-container .pagination .last,#page-container .pagination .next,#page-container .pagination .page,#page-container .pagination .previous { + margin-right:20px; + margin-left: 20px + } +} + +#page-container .page-number .current,#page-container .page-number .num-pages { + font-weight: 600 +} + +html.reader,html.reader #content,html.reader body { + height: 100% +} + +html.reader body { + padding-top: 50px +} + +html.reader #content { + margin-top: 0 +} + +html.reader nav { + margin-top: -50px +} + +html.reader.nav-hidden body { + padding-top: 0 +} + +html.reader.nav-hidden nav { + display: none +} + +html.reader .advertisement.advertisement { + margin: 0; + padding: 0; + padding-top: 20px; + padding-bottom: 20px; + background-color: #324151; + max-width: 100%; + border-radius: 0 +} + +html.reader .reader-bar { + background-color: #3d5064; + color: #fff +} + +html.reader .reader-bar .reader-bar-contents { + max-width: 1280px; + margin-left: auto; + margin-right: auto +} + +html.reader .reader-bar a,html.reader .reader-bar button { + color: #fff; + display: inline-block; + line-height: 20px; + padding: 10px; + min-width: 40px; + min-height: 100%; + border-radius: 0 +} + +html.reader .reader-bar a:hover,html.reader .reader-bar button:hover { + background-color: #516a83 +} + +html.reader .reader-bar .reader-pagination { + display: inline-block +} + +html.reader .reader-bar .reader-pagination .page-number { + vertical-align: top +} + +html.reader .reader-bar .reader-pagination .page-number .current,html.reader .reader-bar .reader-pagination .page-number .num-pages { + font-weight: 700 +} + +html.reader .reader-bar .go-back { + float: left; + margin-right: -40px +} + +html.reader .reader-bar .reader-settings { + float: right; + margin-left: -40px +} + +html.reader #image-container { + -webkit-tap-highlight-color: rgba(255,255,255,0); + text-align: center; + height: 100%; + outline: 0; + height: auto +} + +html.reader #image-container img { + vertical-align: bottom; + height: auto; + max-width: 100%; + user-select: none +} + +html.reader #image-container.full-height { + height: -moz-calc(100% + 10px + 40px); + height: -webkit-calc(100% + 10px + 40px); + height: calc(100% + 10px + 40px) +} + +html.reader #image-container.fit-horizontal { + height: auto +} + +html.reader #image-container.fit-both { + height: -moz-calc(100% + 10px); + height: -webkit-calc(100% + 10px); + height: calc(100% + 10px) +} + +html.reader #image-container.fit-both img { + max-height: 100%; + width: auto +} + +html.theme-blue #thumbnail-container .lazyload,html.theme-blue .lazyload { + background-color: #303f4d +} + +html.theme-blue #thumbnail-container .lazyload-loading,html.theme-blue .lazyload-loading { + background-color: #364657 +} + +html.theme-blue,html.theme-blue body { + color: #d9d9d9; + background-color: #202a34 +} + +html.theme-blue a.btn { + color: #fff +} + +html.theme-blue a,html.theme-blue ul.nav.navbar-nav>li>a { + color: #d9d9d9 +} + +html.theme-blue ul.nav.navbar-nav>li.active>a { + color: #fff +} + +html.theme-blue .container { + background-color: #2a3744 +} + +html.theme-blue .gallery-thumbnail { + background-color: #303f4d +} + +html.theme-blue .tag { + color: #d9d9d9; + background: #3d5064 +} + +html.theme-blue .tag:hover { + background: #4b627a +} + +html.theme-blue #tag-container { + -webkit-column-rule-color: #3a4b5d; + -moz-column-rule-color: #3a4b5d; + -ms-column-rule-color: #3a4b5d; + -o-column-rule-color: #3a4b5d; + column-rule-color: #3a4b5d +} + +html.theme-blue #tag-container section:target { + border-color: #475d73; + background: #344454 +} + +html.theme-blue #tag-container section:target .tag:hover { + background: #3d5064 +} + +html.theme-blue #tag-container .tag { + color: #d9d9d9 +} + +html.theme-blue #tag-container .tag .count { + color: grey +} + +html.theme-blue #tag-container .tag:hover { + background: #344454 +} + +html.theme-blue .gallery:hover .caption { + box-shadow: 0 10px 20px rgba(0,0,0,.5) +} + +html.theme-blue .caption { + background-color: #3d5064; + color: #d9d9d9 +} + +html.theme-blue .pagination .first:hover,html.theme-blue .pagination .last:hover,html.theme-blue .pagination .next:hover,html.theme-blue .pagination .page:hover,html.theme-blue .pagination .previous:hover { + background-color: #3d5064 +} + +html.theme-blue .pagination .first.current,html.theme-blue .pagination .last.current,html.theme-blue .pagination .next.current,html.theme-blue .pagination .page.current,html.theme-blue .pagination .previous.current { + background-color: #344454 +} + +html.theme-blue .alphabetical-pagination a:hover,html.theme-blue .alphabetical-pagination span:hover { + background-color: #3d5064 +} + +html.theme-blue .alphabetical-pagination a.disabled,html.theme-blue .alphabetical-pagination span.disabled { + color: #737373 +} + +html.theme-blue .alphabetical-pagination a.current,html.theme-blue .alphabetical-pagination span.current { + background-color: #344454 +} + +html.theme-blue .comment:target { + background-color: #38495a +} + +html.theme-blue .form-control { + border: none; + background-color: inherit +} + +html.theme-blue code { + color: #a5b6c8; + border: none; + background-color: #3d5064 +} + +html.theme-blue #favorites-search input { + background-color: #fff +} + +html.theme-blue .modal-inner { + background-color: #2a3744 +} + +html.theme-blue .modal-inner .buttons { + background-color: #38495a +} + +html.theme-black,html.theme-black body { + color: #d9d9d9; + background-color: #0d0d0d +} + +html.theme-black a { + color: #d9d9d9 +} + +html.theme-black nav { + background-color: #1f1f1f +} + +html.theme-black nav a:hover { + background-color: #383838 +} + +html.theme-black nav .dropdown-menu.open { + box-shadow: 0 0 30px rgba(0,0,0,.5); + background-color: #383838 +} + +html.theme-black nav .dropdown-menu.open:before { + border-bottom-color: #383838 +} + +html.theme-black nav .dropdown-menu.open a:hover { + background-color: #525252 +} + +html.theme-black .tag-autocomplete .autocomplete-wrapper .dropdown { + background-color: #383838 +} + +html.theme-black #thumbnail-container,html.theme-black .container { + 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-thumbnail { + background-color: #212121 +} + +html.theme-black .tag { + color: #d9d9d9; + background: #4d4d4d +} + +html.theme-black .tag:hover { + background: #666 +} + +html.theme-black .tag .count { + color: grey +} + +html.theme-black #tag-container { + -webkit-column-rule-color: #2e2e2e; + -moz-column-rule-color: #2e2e2e; + -ms-column-rule-color: #2e2e2e; + -o-column-rule-color: #2e2e2e; + column-rule-color: #2e2e2e +} + +html.theme-black #tag-container section:target { + border-color: #404040; + background: #262626 +} + +html.theme-black #tag-container section:target .tag:hover { + background: #333 +} + +html.theme-black #tag-container .tag { + color: #d9d9d9 +} + +html.theme-black #tag-container .tag .count { + color: grey +} + +html.theme-black #tag-container .tag:hover { + background: #4d4d4d +} + +html.theme-black .gallery:hover .caption { + box-shadow: 0 10px 20px rgba(0,0,0,.5) +} + +html.theme-black .caption { + background-color: #404040; + color: #d9d9d9 +} + +html.theme-black #info-container section:not(:last-of-type) { + border-bottom: 1px solid #333 +} + +html.theme-black .pagination .first:hover,html.theme-black .pagination .last:hover,html.theme-black .pagination .next:hover,html.theme-black .pagination .page:hover,html.theme-black .pagination .previous:hover { + background-color: #333 +} + +html.theme-black .pagination .first.current,html.theme-black .pagination .last.current,html.theme-black .pagination .next.current,html.theme-black .pagination .page.current,html.theme-black .pagination .previous.current { + background-color: #262626 +} + +html.theme-black .alphabetical-pagination a:hover,html.theme-black .alphabetical-pagination span:hover { + background-color: #333 +} + +html.theme-black .alphabetical-pagination a.disabled,html.theme-black .alphabetical-pagination span.disabled { + color: #737373 +} + +html.theme-black .alphabetical-pagination a.current,html.theme-black .alphabetical-pagination span.current { + background-color: #262626 +} + +html.theme-black .comment:target { + background-color: #2b2b2b +} + +html.theme-black .form-control { + border: none; + background-color: inherit +} + +html.theme-black .btn { + color: #fff +} + +html.theme-black .btn-secondary { + background-color: #4d4d4d +} + +html.theme-black .btn-secondary:focus,html.theme-black .btn-secondary:hover { + background-color: #595959 +} + +html.theme-black #footer-container .footer-item a { + color: #333 +} + +html.theme-black #footer-container .footer-item a:hover { + color: #666 +} + +html.theme-black code { + color: #ed2553; + border: none; + background-color: #292929 +} + +html.theme-black #favorites-search input { + background-color: #fff +} + +html.theme-black.reader .reader-bar { + background-color: #383838 +} + +html.theme-black.reader .reader-bar a:hover,html.theme-black.reader .reader-bar button:hover { + background-color: #525252 +} + +html.theme-black.reader .advertisement { + background-color: #292929 +} + +html.theme-black .modal-inner { + background-color: #1f1f1f +} + +html.theme-black .modal-inner .buttons { + background-color: #383838 +} \ No newline at end of file diff --git a/nhentai/viewer/main.html b/nhentai/viewer/main.html new file mode 100644 index 0000000..3f170a5 --- /dev/null +++ b/nhentai/viewer/main.html @@ -0,0 +1,37 @@ + + + + + + + + + nHentai + » Viewer + + {STYLE} + + + +
+ +

Main Folder({COUNT})

+ +
+ + {PICTURE} + + +
+ +
+ + + + \ No newline at end of file From 0cda30385b043e59e1dbbc1a6cf3146bb90fba39 Mon Sep 17 00:00:00 2001 From: symant233 Date: Sun, 5 May 2019 21:01:16 +0800 Subject: [PATCH 03/21] Main viewer generator --- nhentai/utils.py | 48 +++++++++++++++++++++++++++++++++++++++- nhentai/viewer/main.html | 15 +++---------- 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/nhentai/utils.py b/nhentai/utils.py index 4239e91..00e6b00 100644 --- a/nhentai/utils.py +++ b/nhentai/utils.py @@ -86,7 +86,53 @@ def generate_main_html(output_dir='.'): With a link to thier `index.html`. Default output folder will be the CLI path. """ - pass + count = 0 + image_html = '' + main = readfile('viewer/main.html') + css = readfile('viewer/main.css') + element = '\n\ + \n' + + os.chdir(output_dir) # switch to given dir + doujinshi_dirs = next(os.walk('.'))[1] + # https://stackoverflow.com/questions/141291/how-to-list-only-top-level-directories-in-python + + for folder in doujinshi_dirs: + files = os.listdir(folder) + if 'index.html' in files: + count += 1 + else: + logger.warning('{} folder does not have index.html, try use --html arg first.'\ + .format(folder)) + continue + image = files[0] # 001.jpg or 001.png + if folder is not None: + title = folder.replace('_', ' ') + if sys.version_info < (3, 0): + title = title.encode('utf-8') + else: + title = 'nHentai HTML Viewer' + image_html += element.format(FOLDER=folder, IMAGE=image, TITLE=title) + + data = main.format(STYLES=css, COUNT=count, PICTURE=image_html) + try: + if sys.version_info < (3, 0): + with open('./main.html', 'w') as f: + f.write(data) + else: + with open('./main.html', 'wb') as f: + f.write(data.encode('utf-8')) + logger.log(15, 'Main Viewer has been write to \'{0}\''.format('./main.html')) + except Exception as e: + logger.warning('Writen Main Viewer failed ({})'.format(str(e))) + logger.info('==>Process finished.') def generate_cbz(output_dir='.', doujinshi_obj=None, rm_origin_dir=False): if doujinshi_obj is not None: diff --git a/nhentai/viewer/main.html b/nhentai/viewer/main.html index 3f170a5..d1bf5eb 100644 --- a/nhentai/viewer/main.html +++ b/nhentai/viewer/main.html @@ -5,11 +5,9 @@ - - nHentai - » Viewer - - {STYLE} + nHentai » Viewer + + @@ -20,13 +18,6 @@
{PICTURE} -
From 0150e79c49003039340093daa4b50b21511d57d0 Mon Sep 17 00:00:00 2001 From: symant233 Date: Sun, 5 May 2019 21:10:24 +0800 Subject: [PATCH 04/21] Add main viewer sources --- MANIFEST.in | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 60ce4b8..cf58764 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,7 @@ -include README.md -include requirements.txt -include nhentai/viewer/index.html -include nhentai/viewer/styles.css -include nhentai/viewer/scripts.js +include README.md +include requirements.txt +include nhentai/viewer/index.html +include nhentai/viewer/styles.css +include nhentai/viewer/scripts.js +include nhentai/viewer/main.html +include nhentai/viewer/main.css From c32b51657585ce615e08d0823d6fb5138c0b7944 Mon Sep 17 00:00:00 2001 From: symant233 Date: Sun, 5 May 2019 21:47:23 +0800 Subject: [PATCH 05/21] js return to prev page press 'q' --- nhentai/command.py | 4 ++-- nhentai/viewer/scripts.js | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/nhentai/command.py b/nhentai/command.py index a325f64..4175bd1 100644 --- a/nhentai/command.py +++ b/nhentai/command.py @@ -10,7 +10,7 @@ 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 +from nhentai.utils import generate_html, generate_cbz, generate_main_html def main(): @@ -66,7 +66,7 @@ def main(): generate_html(options.output_dir, doujinshi) elif options.is_cbz: generate_cbz(options.output_dir, doujinshi, options.rm_origin_dir) - + generate_main_html(options.output_dir) if not platform.system() == 'Windows': logger.log(15, '🍻 All done.') else: diff --git a/nhentai/viewer/scripts.js b/nhentai/viewer/scripts.js index 9dc516c..cb49af5 100644 --- a/nhentai/viewer/scripts.js +++ b/nhentai/viewer/scripts.js @@ -46,14 +46,16 @@ document.getElementById('image-container').onclick = event => { document.onkeypress = event => { switch (event.key.toLowerCase()) { // Previous Image + case 'w': case 'a': changePage(currentPage - 1); break; - + case 'q': + window.history.go(-1); + break; // Next Image case ' ': - case 'esc': // future close page function - case 'enter': + case 's': case 'd': changePage(currentPage + 1); break; From 6053e302eea9c666b89232052b401600b7f2aece Mon Sep 17 00:00:00 2001 From: symant233 Date: Sun, 5 May 2019 22:02:24 +0800 Subject: [PATCH 06/21] fix output_dir make gen-main error --- nhentai/utils.py | 6 +++++- nhentai/viewer/scripts.js | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/nhentai/utils.py b/nhentai/utils.py index 00e6b00..98a239a 100644 --- a/nhentai/utils.py +++ b/nhentai/utils.py @@ -100,7 +100,11 @@ def generate_main_html(output_dir='.'): \n\ \n' - os.chdir(output_dir) # switch to given dir + if output_dir == '': + os.chdir('.') + else: + os.chdir(output_dir) + # switch to given dir doujinshi_dirs = next(os.walk('.'))[1] # https://stackoverflow.com/questions/141291/how-to-list-only-top-level-directories-in-python diff --git a/nhentai/viewer/scripts.js b/nhentai/viewer/scripts.js index cb49af5..8e3f8bf 100644 --- a/nhentai/viewer/scripts.js +++ b/nhentai/viewer/scripts.js @@ -50,6 +50,7 @@ document.onkeypress = event => { case 'a': changePage(currentPage - 1); break; + // Return to previous page case 'q': window.history.go(-1); break; From a5eba9406435fa9b702d79d3f78524fa4b178a8e Mon Sep 17 00:00:00 2001 From: symant233 Date: Mon, 6 May 2019 15:41:26 +0800 Subject: [PATCH 07/21] clean unused style for main.css --- nhentai/viewer/main.css | 1642 +------------------------------------- nhentai/viewer/main.html | 6 +- 2 files changed, 6 insertions(+), 1642 deletions(-) diff --git a/nhentai/viewer/main.css b/nhentai/viewer/main.css index 3ec6c77..60494e3 100644 --- a/nhentai/viewer/main.css +++ b/nhentai/viewer/main.css @@ -1,8 +1,5 @@ -/* -Original from https://static.nhentai.net/css/main_style.9bb9b703e601.css -This `.css` file need to delete some unused elements for our Viewer. -Please fork the project to make contributions. -*/ +/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */ +/* Original from https://static.nhentai.net/css/main_style.9bb9b703e601.css */ html { font-family: sans-serif; line-height: 1.15; @@ -14,34 +11,11 @@ body { margin: 0 } -article,aside,footer,header,nav,section { - display: block -} - h1 { font-size: 2em; margin: .67em 0 } -figcaption,figure,main { - display: block -} - -figure { - margin: 1em 40px -} - -hr { - box-sizing: content-box; - height: 0; - overflow: visible -} - -pre { - font-family: monospace,monospace; - font-size: 1em -} - a { background-color: transparent; -webkit-text-decoration-skip: objects @@ -70,15 +44,6 @@ code,kbd,samp { font-size: 1em } -dfn { - font-style: italic -} - -mark { - background-color: #ff0; - color: #000 -} - small { font-size: 80% } @@ -102,171 +67,6 @@ img { border-style: none } -svg:not(:root) { - overflow: hidden -} - - -@media (min-width: 1201px) { - nav form { - margin-right:10px; - width: 25% - } - - nav #hamburger,nav .menu .dropdown { - display: none - } -} - -@media (min-width: 601px) and (max-width:1200px) { - nav form { - width:35% - } - - nav .menu .desktop { - display: none - } - - nav .menu .dropdown-menu { - display: none - } - - nav #hamburger { - display: none - } - - nav .dropdown { - position: relative - } - - nav .dropdown .dropdown-menu.open { - display: inline-block; - position: absolute; - left: 0; - top: 100%; - border-radius: 3px; - padding-left: 0; - text-align: left; - background-color: #2e3c4a; - z-index: 3; - box-shadow: 0 0 30px rgba(0,0,0,.5) - } - - nav .dropdown .dropdown-menu.open:before { - position: absolute; - content: ''; - pointer-events: none; - bottom: 100%; - left: 15px; - border-color: transparent transparent #2e3c4a transparent; - border-style: solid; - border-width: 10px 10px 10px 10px; - height: 0; - width: 0 - } - - nav .dropdown .dropdown-menu.open li { - width: 100% - } - - nav .dropdown .dropdown-menu.open li a { - line-height: 2; - height: auto; - width: 100% - } - - nav .dropdown .dropdown-menu.open li:first-of-type a { - border-top-right-radius: 3px; - border-top-left-radius: 3px - } - - nav .dropdown .dropdown-menu.open li:last-of-type a { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px - } -} - -@media (max-width: 600px) { - nav { - padding-right:50px - } - - nav form { - width: 100% - } - - nav .collapse { - display: block; - width: 100%; - overflow: hidden; - max-height: 0; - transition: max-height .5s - } - - nav .collapse.open { - max-height: 370px - } - - nav .collapse .menu.left,nav .collapse .menu.right { - float: none - } - - nav .collapse .menu .dropdown { - display: none - } - - nav .collapse .menu li { - display: block - } - - nav .collapse .menu li a { - width: 100%; - height: 35px; - line-height: 35px - } -} - -#settings-container { - text-align: left -} - -#settings-container input[type=email],#settings-container input[type=password],#settings-container input[type=text] { - width: 100%; - height: 30px; - padding-left: 10px; - border-radius: 3px -} - -#settings-container h2 { - text-align: center -} - -#settings-container form { - max-width: 800px; - margin-right: auto; - margin-left: auto -} - -#user-container { - overflow: auto -} - -@media (min-width: 600px) { - .user-info { - text-align:left; - float: left - } - - .bigavatar { - float: left; - margin: 2em - } -} - -.form-group { - margin: 20px -} - label { display: block; font-weight: 700; @@ -299,10 +99,6 @@ body,html { -moz-osx-font-smoothing: grayscale } -button,input,textarea { - font-family: 'Noto Sans',sans-serif -} - a { text-decoration: none; color: #34495e @@ -327,116 +123,10 @@ code { background-color: #fef0f3 } -textarea { - resize: none -} - blockquote { border: 0 } -form.inline { - display: inline -} - -.btn { - display: inline-block; - vertical-align: middle; - font-weight: 700; - cursor: pointer; - padding: 0; - padding-right: 12px; - padding-left: 12px; - color: #fff; - border: 0; - border-radius: 3px; - outline: 0; - font-size: 100%; - height: 40px; - line-height: 40px; - margin: 3px; - -webkit-font-smoothing: antialiased -} - -.btn:hover { - color: #fff; - background: #f15478 -} - -.btn.btn-unstyled { - font-weight: 400; - line-height: 1; - height: auto; - color: inherit; - padding: 0; - margin: 0; - background: 0 0; - cursor: pointer; - border: none -} - -.btn.btn-thin { - font-weight: 400; - height: auto; - line-height: 1; - margin-top: 5px; - margin-bottom: 10px; - padding-top: 5px; - padding-right: 6px; - padding-bottom: 5px; - padding-left: 6px -} - -.btn.btn-primary { - background-color: #ed2553 -} - -.btn.btn-primary:active,.btn.btn-primary:focus,.btn.btn-primary:hover { - color: #fff; - background: #f15478 -} - -.btn.btn-primary.disabled,.btn.btn-primary:disabled { - cursor: default; - background-color: #be546c -} - -.btn.btn-secondary { - background-color: #475d73 -} - -.btn.btn-secondary:active,.btn.btn-secondary:focus,.btn.btn-secondary:hover { - background: #516a83 -} - -.btn.btn-secondary.disabled,.btn.btn-secondary:disabled { - cursor: default; - background-color: #555d65 -} - -.btn.btn-square { - width: 40px; - height: 40px; - margin: 5px; - vertical-align: middle -} - -.edit { - font-size: 15px; - padding: 5px; - padding-top: 2px; - padding-bottom: 2px; - vertical-align: middle; - color: #fff; - border-radius: 3px; - background: #2a3744 -} - -.edit:hover { - color: #fff; - background: #3d5064 -} - .container { display: block; clear: both; @@ -451,167 +141,6 @@ form.inline { max-width: 1200px } -@media screen and (orientation: landscape) { - .container { - padding-left:calc(5px + constant(safe-area-inset-left)); - padding-right: calc(5px + constant(safe-area-inset-right)) - } -} - -.container.left { - text-align: left -} - -.container.right { - text-align: right -} - -@media screen and (max-width: 1200px) { - .container { - border-radius:0 - } -} - -.container.advertisement.advertisement { - overflow: hidden; - margin-right: auto; - margin-left: auto; - text-align: center; - background-color: inherit; - min-height: 90px -} - -.container.advertisement.advertisement iframe { - display: block; - margin-right: auto; - margin-left: auto -} - -.container:last-of-type { - margin-bottom: 0 -} - -.invisible { - z-index: -1; - visibility: hidden -} - -.pagination { - font-size: 1.3em; - margin-bottom: 2em; - margin-top: 2em -} - -.pagination .first,.pagination .last,.pagination .next,.pagination .page,.pagination .previous { - display: inline-block; - min-width: 35px; - padding: 5px -} - -.pagination .first.current,.pagination .last.current,.pagination .next.current,.pagination .page.current,.pagination .previous.current { - font-weight: 700; - border-radius: 100%; - background-color: #f2f5f5 -} - -.pagination .first:hover,.pagination .last:hover,.pagination .next:hover,.pagination .page:hover,.pagination .previous:hover { - border-radius: 100%; - background-color: #f8f9fa -} - -.alphabetical-pagination { - margin-bottom: 0; - padding-left: 0; - list-style: none -} - -.alphabetical-pagination li { - display: inline-block -} - -.alphabetical-pagination li a,.alphabetical-pagination li span { - font-size: 1.15em; - display: inline-block; - min-width: 26px; - padding: 2px; - text-align: center -} - -.alphabetical-pagination li a:hover,.alphabetical-pagination li span:hover { - border-radius: 100%; - background-color: #f5f7f7 -} - -.alphabetical-pagination li a.current,.alphabetical-pagination li span.current { - border-radius: 100%; - font-weight: 700 -} - -.alphabetical-pagination li a.disabled,.alphabetical-pagination li span.disabled { - color: #999 -} - -.tag { - display: inline-block; - border-radius: 3px; - color: #fff; - margin: 2px; - padding: 2px 6px 3px; - background: #364657 -} - -.tag:hover { - color: #fff; - background: #556f89 -} - -.tag .count { - font-weight: 400; - color: #a5b6c8 -} - -#footer-container { - margin-top: 15px; - margin-bottom: 6px; - background-color: inherit -} - -#footer-container .footer-item a { - margin: 10px; - color: #5e7980 -} - -.thumb-container { - text-align: center -} - -.thumb-container img { - margin-top: 2px; - margin-right: 2px; - margin-left: 2px -} - -.blacklisted-tag { - text-decoration: line-through -} - -.blacklisted.gallery { - opacity: .1; - -webkit-filter: blur(5px); - filter: blur(5px) -} - -.blacklisted.gallery:hover { - opacity: .2; - -webkit-filter: blur(0); - filter: blur(0) -} - -.blacklisted.tag { - opacity: .5; - text-decoration: line-through -} - .gallery,.gallery-favorite,.thumb-container { display: inline-block; vertical-align: top @@ -689,1059 +218,15 @@ form.inline { box-shadow: 0 10px 20px rgba(100,100,100,.5) } -.gallery-favorite .btn { - margin-top: 20px -} - .gallery-favorite .gallery { width: 100% } -.gallery[data-tags*="12227"] .caption:before,.gallery[data-tags*="29963"] .caption:before,.gallery[data-tags*="6346"] .caption:before { - content: ""; - display: inline-block; - vertical-align: middle; - z-index: 2; - background-size: contain; - width: 18px; - height: 12px -} - -.gallery[data-tags*="6346"] .caption:before { - background-image: url(data:image/gif;base64,R0lGODlhSAAwAOZhAO1UZPn6/PX+//X8//z9/vX4+/X//////+1TY+1PX/X7/vX7/fb///X9/+1SYvf4++1QYOxMXPn7/P7+//Xw9PXx9fGjre5pd/X8/vb4+/GnsPPN1O1OX/CQm+95hvTd4vXy9vGXovTb4PCVoO1TZPPS1+1OXu1QYfPT2fTm6/Ggqu1dbfX5/O5icPCSnfCTne1RYvTq7u5hcfGkrvTv8+5jce1UZfTr7/To7fKqsvPJz+1WZvPO0/CTnvXt8fPP1e93g/K0vPLEy/LEzO1PYO1YaO+Aje5od+1ZaPLDy+5gb/LFzO5ice+Klu1XZ++Cju1YZ+5hcPXs8PGirOxKW++JlexLXO+CjfK2v/K2vfXs8e+BjPG0u+5hb+1VZfb4+vX3+v///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAGEALAAAAABIADAAAAf/gGFhBwQBD1+IiYqLjI2Oj5CLDwEEB4JhEwFfYJydnp+goaKjpKNfARODEqWsra6vnRKEm7C1trBfhbe7vKQBGb3BwmCHw8a3iMfKr8nLzqTNz9Kf0dPW1dbS2NnO28MFCw0CBgINCwXdtMcFAz5LFi9NLxZCWgMsyt68GCBYTxdIIFhJAOWCkSAVBhzTdwsDjhBMIiRwAMALAAcJInQZkULhMIa1GtzoAYCIl5MoUUIg0SGGgI/qeimokGMHgpQ4T97UQEGBMJCvBJSoASOnUQcteAhA1wtoqwUUZnCwYdSoCRU0fDaNucvABw83q+ZEAESEgWBOWRlAIaOiWJwA0Fb8YICW6y0GGyy+zXlCB92twvDq3Zuy719eaUutbUsYZdy5dYV5BdtYZ9mzgINBlUq18dWskYUJJdoYqVKmiO3emlkzrNidPX+qviWSJASxK1u+lH3MYYgoEilaxBhBCUePvI8NAJHlyoUiCagkcHJkC5eEC2fzYudjiAUXVVxMSSLlXj7tvcCJI2cOdXZu19DDDz0/Xf1nie/jkq/fVv7+rXwBDIDGZBAAgcZQwh+CpeRywCoM8iILJppEWMspqQhCiCGRdOjhh5BMUokggQAAOw==) -} - -.gallery[data-tags*="12227"] .caption:before { - background-image: url(data:image/gif;base64,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) -} - -.gallery[data-tags*="29963"] .caption:before { - background-image: url(data:image/gif;base64,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) -} - -.gallery-edit { - text-align: left; - overflow: auto -} - -.gallery-edit .gallery { - float: left -} - -.gallery-edit .tag-container { - display: inline-block -} - -#info-container { - height: auto; - padding: 40px; - padding-top: 10px; - padding-bottom: 20px; - text-align: left -} - -#info-container section.text { - padding-top: 10px; - padding-bottom: 10px -} - -#info-container section:not(:last-of-type) { - padding-bottom: 10px; - border-bottom: 1px solid #dde4e6 -} - -#info-container h1 { - font-size: 2em -} - -#info-container h2 { - font-size: 1.6em -} - -#info-container h3 { - font-size: 1.4em -} - -#info-container li { - margin: 5px -} - -#info-container a { - color: #ed2553 -} - -#info-container #thanks { - margin-top: 20px; - text-align: center -} - -.sort { - margin-top: -15px -} - -.sort:before { - content: ""; - display: block; - clear: both; - margin-top: 1em -} - -.sort a { - font-size: 20px; - display: inline-block; - padding: 20px -} - -.sort a.active { - font-weight: 700 -} - -.sort+.container { - margin-top: 0 -} - -#info { - height: auto; - padding: 10px; - text-align: left -} - -#info h1 { - font-size: 20px -} - -#info h2 { - font-size: 17px; - margin-top: 10px; - margin-bottom: 20px -} - -#info .field-name { - font-weight: 700; - margin-top: 5px; - margin-bottom: 5px -} - -#info .buttons { - margin-top: 30px -} - -#info .buttons .btn { - min-width: 120px; - text-align: center; - margin-left: 0; - margin-right: 5px; - margin-bottom: 10px -} - -#info a { - font-weight: 700 -} - -#profile-update input[type=file]#avatar { - display: none -} - -#profile-update #avatar-upload { - overflow: auto; - width: 200px; - margin-bottom: 10px -} - -#profile-update #avatar-upload img { - margin-bottom: 10px -} - -#profile-update #avatar-upload .btn { - width: 110px -} - -#profile-update #avatar-upload label[for=avatar] { - float: left; - width: 70px; - padding-right: 5px; - padding-left: 5px -} - -#profile-update #avatar-upload label[for=avatar-clear] { - float: right -} - -#profile-update #avatar-upload #avatar-clear { - position: relative; - top: 2px; - margin: 0; - margin-left: 3px -} - -#bigcontainer { - margin-bottom: 25px; - padding: 20px; - padding-top: 30px; - padding-bottom: 30px -} - -#bigcontainer form { - display: inline-block -} - -@media screen and (min-width: 768px) { - #bigcontainer #cover,#bigcontainer #info-block { - display:inline-block; - width: 48%; - vertical-align: top - } -} - -@media screen and (max-width: 500px) { - #bigcontainer #cover,#bigcontainer #info-block { - width:100%; - margin: .5px - } -} - -#bigcontainer #cover img,#bigcontainer #info-block img { - max-width: 100% -} - -.fa-heart,.fa-heart-o { - margin: 2px -} - -.lazyload { - background-color: #e3e9eb -} - -.lazyload-loading { - background: #dae2e4 -} - -#thumbnail-container { - margin-bottom: 25px; - padding: 15px; - text-align: left; - background-color: #2a3744 -} - -@media screen and (max-width: 980px) { - #thumbnail-container { - padding:5px; - padding-top: 10px; - border-radius: 0 - } -} - -#thumbnail-container .lazyload { - background-color: #303f4d -} - -#thumbnail-container .lazyload-loading { - background-color: #364657 -} - -#thumbnail-container .gallerythumb { - display: inline-block; - margin-bottom: 3px; - vertical-align: middle -} - -#thumbnail-container .gallerythumb a { - display: inline-block -} - -#related-container { - margin-bottom: 25px; - padding-bottom: 20px -} - -.commentform { - max-width: 100% -} - -.login-comment { - text-decoration: underline -} - -#comment-container { - padding: 20px -} - -#comment-container #id_body { - width: 85%; - height: 90px; - margin-bottom: 5px; - padding: 10px; - border: 0; - border-radius: 3px; - outline: 0; - -webkit-appearance: none -} - -.commentbutton { - width: 120px; - height: 40px; - color: #fff; - border: 0; - border-radius: 3px -} - -.comment { - display: flex; - text-align: left; - margin-bottom: 10px; - padding: 8px -} - -.comment .header { - display: flex; - flex-direction: row; - margin-bottom: 5px -} - -.comment .header time { - margin-left: .5em; - opacity: .6 -} - -.comment .header .right { - margin-left: auto; - text-align: right -} - -.comment .header .right .comment-delete { - opacity: .6 -} - -.comment .header .right .comment-delete:hover { - opacity: .9 -} - -.comment .body-wrapper { - flex: 1 -} - -.comment .body-wrapper .body { - word-break: break-word -} - -.comment .body-wrapper .body a { - color: #ed2553 -} - -.comment .body-wrapper .body a:hover { - color: #ef3d66; - text-decoration: underline -} - -.comment:target { - background-color: #d7e0e2 -} - -#favorites-search-bar { - width: 200px; - height: 35px; - padding: 10px; - border: 0; - border-radius: 3px; - outline: 0; - outline-width: 0; - outline-style: none; - background-color: #dae2e4; - -webkit-appearance: none -} - -#favorites-search-form { - margin-bottom: 20px -} - -#favorites-search { - display: inline-block -} - -#favorites-search input { - background-color: #ecf0f1 -} - -#favorites-search-button { - width: 40px; - height: 35px -} - -#favorites-random-button { - margin-left: -15px -} - -.remove-button { - margin-top: 5px; - margin-bottom: 10px; - background-color: #e32636 -} - -.remove-button:hover { - text-decoration: none; - background-color: #e6414f -} - -#favcontainer { - padding: 10px; - padding-bottom: 40px -} - -.removed { - opacity: .5 -} - -#tag-container { - padding-bottom: 5px; - -webkit-columns: 200px; - -moz-columns: 200px; - -ms-columns: 200px; - -o-columns: 200px; - columns: 200px; - -webkit-column-rule-color: #e2e8e9; - -webkit-column-rule-width: 1px; - -webkit-column-rule-style: solid; - -moz-column-rule-color: #e2e8e9; - -moz-column-rule-width: 1px; - -moz-column-rule-style: solid; - -ms-column-rule-color: #e2e8e9; - -ms-column-rule-width: 1px; - -ms-column-rule-style: solid; - -o-column-rule-color: #e2e8e9; - -o-column-rule-width: 1px; - -o-column-rule-style: solid; - column-rule-color: #e2e8e9; - column-rule-width: 1px; - column-rule-style: solid -} - -#tag-container .tag { - display: block; - padding-top: 5px; - padding-bottom: 5px; - border-radius: 5px; - -webkit-column-break-inside: avoid; - -moz-column-break-inside: avoid; - page-break-inside: avoid; - background-color: transparent; - color: #34495e -} - -#tag-container .tag:hover { - background-color: #f5f7f7 -} - -#tag-container section { - border: 1px solid transparent; - border-radius: 5px -} - -#tag-container section:first-of-type h2 { - margin-top: 0 -} - -#tag-container section:target { - border-color: #cfd9db; - background-color: #f5f7f7 -} - -#tag-container section:target .tag:hover { - background-color: #fff -} - -input,input:focus { - -webkit-transition: none; - -moz-transition: none; - transition: none; - border: none; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; - outline: 0 -} - -div.form-control { - background-color: transparent -} - -.container.error h1 { - font-size: 50px -} - -.container.error blockquote { - font-size: 25px; - font-style: italic -} - -.container.error img { - max-width: 100% -} - -.nobold { - font-weight: 400 -} - -.hidden { - display: none -} - -#favorites-search { - padding-left: 20px; - padding-right: 60px; - margin-left: auto; - margin-right: auto; - max-width: 300px -} - -.avatar { - display: inline-block; - position: relative -} - -.avatar img { - display: inline-block; - vertical-align: middle; - width: 50px; - height: 50px; - margin-right: 10px -} - -.avatar .username { - display: inline-block; - vertical-align: middle -} - -.edit-queue { - width: 100% -} - -.edit-queue .user img { - vertical-align: middle; - margin-right: 5px -} - -.edit-queue tr td:nth-child(2) { - width: 20% -} - -.edit-queue tr td:nth-child(6) { - min-width: 160px -} - -.edit-queue tr.voted { - display: none -} - -.g-recaptcha { - display: inline-block -} - -.tag-autocomplete { - display: inline-block -} - -.tag-autocomplete .tag-wrapper { - display: inline-block -} - -.tag-autocomplete .autocomplete-wrapper { - position: relative; - display: inline-block -} - -.tag-autocomplete .autocomplete-wrapper.hidden { - display: none -} - -.tag-autocomplete .autocomplete-wrapper .tag-input { - font-weight: 400; - margin: 0; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0 -} - -.tag-autocomplete .autocomplete-wrapper .dropdown { - z-index: 2; - position: absolute; - left: 0; - width: auto; - right: 0; - background-color: #364657; - margin-top: 0; - list-style-type: none; - padding-left: 0; - border-bottom-left-radius: 3px; - border-bottom-right-radius: 3px; - box-shadow: 0 10px 20px rgba(0,0,0,.5) -} - -.tag-autocomplete .autocomplete-wrapper .dropdown li.active>a.tag { - background: #506984!important -} - -.tag-autocomplete .autocomplete-wrapper .dropdown li.disabled { - font-style: italic; - padding: 2px 6px 3px; - color: gray -} - -.tag-container .name { - font-weight: 700 -} - -.tag-container .tag.tag-deleted { - background-color: #643d3d; - text-decoration: line-through -} - -.tag-container .tag.tag-added { - background-color: #3d643f -} - -.tag-container .tag.tag-created { - background-color: #7c7b36 -} - -.tag-container .tag.tag-created .count { - color: #b3b3b3 -} - -.tag-container .tag.tag-new { - cursor: pointer -} - -.tag.tag-input { - margin-bottom: 0 -} - -#messages { - position: fixed; - top: 0; - width: 100%; - z-index: 20 -} - -#messages .alert { - display: flex; - padding: 15px; - margin-top: 10px; - border: 1px solid transparent; - border-radius: 4px -} - -#messages .alert.alert-info,#messages .alert.alert-success { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6 -} - -#messages .alert.alert-danger { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1 -} - -#messages .alert.alert-warning { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc -} - -#messages .alert .alert-close { - margin-left: auto; - color: #34495e -} - -.modal-wrapper { - display: block; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - padding: 10px; - background: rgba(0,0,0,.6) -} - -.modal-wrapper.modal-compact .modal-inner { - display: inline-block -} - -.modal-wrapper.modal-compact .modal-inner h1 { - display: none -} - -.modal-wrapper .modal-inner { - overflow: auto; - background-color: #fff; - border-radius: 5px; - max-width: 600px; - padding-top: 10px; - margin-left: auto; - margin-right: auto; - margin-top: 100px; - box-shadow: 0 0 60px rgba(0,0,0,.5) -} - -.modal-wrapper .modal-inner .contents { - padding: 10px -} - -.modal-wrapper .modal-inner .buttons { - padding: 5px; - margin-top: 10px; - background-color: #e6e6e6 -} - -#chatbanner { - margin: 20px -} - -#chatbanner img { - border: 2px solid #ed2553 -} - -.announcement { - margin-top: 20px; - margin-bottom: 20px; - margin-left: 5px; - margin-right: 5px; - font-size: 18px -} - -.announcement a[href] { - color: #ed2553 -} - -#errors { - text-align: center; - list-style-type: none; - padding-left: 0; - color: #ef808a -} - -select { - font-weight: 400 -} - -#page-container { - margin-bottom: 5px -} - -@media (max-width: 768px) { - #page-container img { - width:100%; - height: auto - } -} - -@media (min-width: 769px) { - #page-container img { - max-width:90%; - height: auto - } -} - -#page-container .pagination { - margin-bottom: 5px; - margin-top: 5px -} - -#page-container .pagination .first,#page-container .pagination .last,#page-container .pagination .next,#page-container .pagination .page,#page-container .pagination .previous { - font-size: 17px; - margin-bottom: 10px; - margin-top: 10px -} - -@media (min-width: 397px) { - #page-container .pagination .first,#page-container .pagination .last,#page-container .pagination .next,#page-container .pagination .page,#page-container .pagination .previous { - margin-right:20px; - margin-left: 20px - } -} - -#page-container .page-number .current,#page-container .page-number .num-pages { - font-weight: 600 -} - -html.reader,html.reader #content,html.reader body { - height: 100% -} - -html.reader body { - padding-top: 50px -} - -html.reader #content { - margin-top: 0 -} - -html.reader nav { - margin-top: -50px -} - -html.reader.nav-hidden body { - padding-top: 0 -} - -html.reader.nav-hidden nav { - display: none -} - -html.reader .advertisement.advertisement { - margin: 0; - padding: 0; - padding-top: 20px; - padding-bottom: 20px; - background-color: #324151; - max-width: 100%; - border-radius: 0 -} - -html.reader .reader-bar { - background-color: #3d5064; - color: #fff -} - -html.reader .reader-bar .reader-bar-contents { - max-width: 1280px; - margin-left: auto; - margin-right: auto -} - -html.reader .reader-bar a,html.reader .reader-bar button { - color: #fff; - display: inline-block; - line-height: 20px; - padding: 10px; - min-width: 40px; - min-height: 100%; - border-radius: 0 -} - -html.reader .reader-bar a:hover,html.reader .reader-bar button:hover { - background-color: #516a83 -} - -html.reader .reader-bar .reader-pagination { - display: inline-block -} - -html.reader .reader-bar .reader-pagination .page-number { - vertical-align: top -} - -html.reader .reader-bar .reader-pagination .page-number .current,html.reader .reader-bar .reader-pagination .page-number .num-pages { - font-weight: 700 -} - -html.reader .reader-bar .go-back { - float: left; - margin-right: -40px -} - -html.reader .reader-bar .reader-settings { - float: right; - margin-left: -40px -} - -html.reader #image-container { - -webkit-tap-highlight-color: rgba(255,255,255,0); - text-align: center; - height: 100%; - outline: 0; - height: auto -} - -html.reader #image-container img { - vertical-align: bottom; - height: auto; - max-width: 100%; - user-select: none -} - -html.reader #image-container.full-height { - height: -moz-calc(100% + 10px + 40px); - height: -webkit-calc(100% + 10px + 40px); - height: calc(100% + 10px + 40px) -} - -html.reader #image-container.fit-horizontal { - height: auto -} - -html.reader #image-container.fit-both { - height: -moz-calc(100% + 10px); - height: -webkit-calc(100% + 10px); - height: calc(100% + 10px) -} - -html.reader #image-container.fit-both img { - max-height: 100%; - width: auto -} - -html.theme-blue #thumbnail-container .lazyload,html.theme-blue .lazyload { - background-color: #303f4d -} - -html.theme-blue #thumbnail-container .lazyload-loading,html.theme-blue .lazyload-loading { - background-color: #364657 -} - -html.theme-blue,html.theme-blue body { - color: #d9d9d9; - background-color: #202a34 -} - -html.theme-blue a.btn { - color: #fff -} - -html.theme-blue a,html.theme-blue ul.nav.navbar-nav>li>a { - color: #d9d9d9 -} - -html.theme-blue ul.nav.navbar-nav>li.active>a { - color: #fff -} - -html.theme-blue .container { - background-color: #2a3744 -} - -html.theme-blue .gallery-thumbnail { - background-color: #303f4d -} - -html.theme-blue .tag { - color: #d9d9d9; - background: #3d5064 -} - -html.theme-blue .tag:hover { - background: #4b627a -} - -html.theme-blue #tag-container { - -webkit-column-rule-color: #3a4b5d; - -moz-column-rule-color: #3a4b5d; - -ms-column-rule-color: #3a4b5d; - -o-column-rule-color: #3a4b5d; - column-rule-color: #3a4b5d -} - -html.theme-blue #tag-container section:target { - border-color: #475d73; - background: #344454 -} - -html.theme-blue #tag-container section:target .tag:hover { - background: #3d5064 -} - -html.theme-blue #tag-container .tag { - color: #d9d9d9 -} - -html.theme-blue #tag-container .tag .count { - color: grey -} - -html.theme-blue #tag-container .tag:hover { - background: #344454 -} - -html.theme-blue .gallery:hover .caption { - box-shadow: 0 10px 20px rgba(0,0,0,.5) -} - -html.theme-blue .caption { - background-color: #3d5064; - color: #d9d9d9 -} - -html.theme-blue .pagination .first:hover,html.theme-blue .pagination .last:hover,html.theme-blue .pagination .next:hover,html.theme-blue .pagination .page:hover,html.theme-blue .pagination .previous:hover { - background-color: #3d5064 -} - -html.theme-blue .pagination .first.current,html.theme-blue .pagination .last.current,html.theme-blue .pagination .next.current,html.theme-blue .pagination .page.current,html.theme-blue .pagination .previous.current { - background-color: #344454 -} - -html.theme-blue .alphabetical-pagination a:hover,html.theme-blue .alphabetical-pagination span:hover { - background-color: #3d5064 -} - -html.theme-blue .alphabetical-pagination a.disabled,html.theme-blue .alphabetical-pagination span.disabled { - color: #737373 -} - -html.theme-blue .alphabetical-pagination a.current,html.theme-blue .alphabetical-pagination span.current { - background-color: #344454 -} - -html.theme-blue .comment:target { - background-color: #38495a -} - -html.theme-blue .form-control { - border: none; - background-color: inherit -} - -html.theme-blue code { - color: #a5b6c8; - border: none; - background-color: #3d5064 -} - -html.theme-blue #favorites-search input { - background-color: #fff -} - -html.theme-blue .modal-inner { - background-color: #2a3744 -} - -html.theme-blue .modal-inner .buttons { - background-color: #38495a -} - html.theme-black,html.theme-black body { color: #d9d9d9; background-color: #0d0d0d } -html.theme-black a { - color: #d9d9d9 -} - -html.theme-black nav { - background-color: #1f1f1f -} - -html.theme-black nav a:hover { - background-color: #383838 -} - -html.theme-black nav .dropdown-menu.open { - box-shadow: 0 0 30px rgba(0,0,0,.5); - background-color: #383838 -} - -html.theme-black nav .dropdown-menu.open:before { - border-bottom-color: #383838 -} - -html.theme-black nav .dropdown-menu.open a:hover { - background-color: #525252 -} - -html.theme-black .tag-autocomplete .autocomplete-wrapper .dropdown { - background-color: #383838 -} - html.theme-black #thumbnail-container,html.theme-black .container { background-color: #1f1f1f } @@ -1754,52 +239,6 @@ html.theme-black #thumbnail-container .lazyload-loading,html.theme-black .lazylo background-color: #2e2e2e } -html.theme-black .gallery-thumbnail { - background-color: #212121 -} - -html.theme-black .tag { - color: #d9d9d9; - background: #4d4d4d -} - -html.theme-black .tag:hover { - background: #666 -} - -html.theme-black .tag .count { - color: grey -} - -html.theme-black #tag-container { - -webkit-column-rule-color: #2e2e2e; - -moz-column-rule-color: #2e2e2e; - -ms-column-rule-color: #2e2e2e; - -o-column-rule-color: #2e2e2e; - column-rule-color: #2e2e2e -} - -html.theme-black #tag-container section:target { - border-color: #404040; - background: #262626 -} - -html.theme-black #tag-container section:target .tag:hover { - background: #333 -} - -html.theme-black #tag-container .tag { - color: #d9d9d9 -} - -html.theme-black #tag-container .tag .count { - color: grey -} - -html.theme-black #tag-container .tag:hover { - background: #4d4d4d -} - html.theme-black .gallery:hover .caption { box-shadow: 0 10px 20px rgba(0,0,0,.5) } @@ -1809,85 +248,8 @@ html.theme-black .caption { color: #d9d9d9 } -html.theme-black #info-container section:not(:last-of-type) { - border-bottom: 1px solid #333 -} - -html.theme-black .pagination .first:hover,html.theme-black .pagination .last:hover,html.theme-black .pagination .next:hover,html.theme-black .pagination .page:hover,html.theme-black .pagination .previous:hover { - background-color: #333 -} - -html.theme-black .pagination .first.current,html.theme-black .pagination .last.current,html.theme-black .pagination .next.current,html.theme-black .pagination .page.current,html.theme-black .pagination .previous.current { - background-color: #262626 -} - -html.theme-black .alphabetical-pagination a:hover,html.theme-black .alphabetical-pagination span:hover { - background-color: #333 -} - -html.theme-black .alphabetical-pagination a.disabled,html.theme-black .alphabetical-pagination span.disabled { - color: #737373 -} - -html.theme-black .alphabetical-pagination a.current,html.theme-black .alphabetical-pagination span.current { - background-color: #262626 -} - -html.theme-black .comment:target { - background-color: #2b2b2b -} - -html.theme-black .form-control { - border: none; - background-color: inherit -} - -html.theme-black .btn { - color: #fff -} - -html.theme-black .btn-secondary { - background-color: #4d4d4d -} - -html.theme-black .btn-secondary:focus,html.theme-black .btn-secondary:hover { - background-color: #595959 -} - -html.theme-black #footer-container .footer-item a { - color: #333 -} - -html.theme-black #footer-container .footer-item a:hover { - color: #666 -} - html.theme-black code { color: #ed2553; border: none; background-color: #292929 -} - -html.theme-black #favorites-search input { - background-color: #fff -} - -html.theme-black.reader .reader-bar { - background-color: #383838 -} - -html.theme-black.reader .reader-bar a:hover,html.theme-black.reader .reader-bar button:hover { - background-color: #525252 -} - -html.theme-black.reader .advertisement { - background-color: #292929 -} - -html.theme-black .modal-inner { - background-color: #1f1f1f -} - -html.theme-black .modal-inner .buttons { - background-color: #383838 } \ No newline at end of file diff --git a/nhentai/viewer/main.html b/nhentai/viewer/main.html index d1bf5eb..b966780 100644 --- a/nhentai/viewer/main.html +++ b/nhentai/viewer/main.html @@ -7,13 +7,15 @@ nHentai » Viewer - +
-

Main Folder({COUNT})

+

Main Folder({COUNT})

From 2d327359aaa5d26101c74ce17ba461a21eb6e9c7 Mon Sep 17 00:00:00 2001 From: symant233 Date: Tue, 21 May 2019 16:16:58 +0800 Subject: [PATCH 08/21] small fix --- nhentai/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nhentai/utils.py b/nhentai/utils.py index 98a239a..434cf80 100644 --- a/nhentai/utils.py +++ b/nhentai/utils.py @@ -119,7 +119,7 @@ def generate_main_html(output_dir='.'): image = files[0] # 001.jpg or 001.png if folder is not None: title = folder.replace('_', ' ') - if sys.version_info < (3, 0): + if sys.version_info > (3, 0): title = title.encode('utf-8') else: title = 'nHentai HTML Viewer' From 710cc86eaf2c192be0a3d30d860e205cd118be9a Mon Sep 17 00:00:00 2001 From: symant233 Date: Tue, 21 May 2019 17:06:42 +0800 Subject: [PATCH 09/21] fix codec error for py2 --- nhentai/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nhentai/utils.py b/nhentai/utils.py index 434cf80..fe12709 100644 --- a/nhentai/utils.py +++ b/nhentai/utils.py @@ -119,14 +119,14 @@ def generate_main_html(output_dir='.'): image = files[0] # 001.jpg or 001.png if folder is not None: title = folder.replace('_', ' ') - if sys.version_info > (3, 0): - title = title.encode('utf-8') + # if sys.version_info > (3, 0): + # title = title.encode('utf-8') else: title = 'nHentai HTML Viewer' image_html += element.format(FOLDER=folder, IMAGE=image, TITLE=title) - data = main.format(STYLES=css, COUNT=count, PICTURE=image_html) try: + data = main.format(STYLES=css, COUNT=count, PICTURE=image_html) if sys.version_info < (3, 0): with open('./main.html', 'w') as f: f.write(data) From 86b3a092c733f6708ef79d68c9f5608acc399ea5 Mon Sep 17 00:00:00 2001 From: symant233 Date: Sun, 26 May 2019 15:57:50 +0800 Subject: [PATCH 10/21] ignore other folders --- nhentai/utils.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nhentai/utils.py b/nhentai/utils.py index fe12709..ce61aa9 100644 --- a/nhentai/utils.py +++ b/nhentai/utils.py @@ -109,6 +109,8 @@ def generate_main_html(output_dir='.'): # https://stackoverflow.com/questions/141291/how-to-list-only-top-level-directories-in-python for folder in doujinshi_dirs: + if folder[0] is not '[': + continue files = os.listdir(folder) if 'index.html' in files: count += 1 @@ -125,6 +127,9 @@ def generate_main_html(output_dir='.'): title = 'nHentai HTML Viewer' image_html += element.format(FOLDER=folder, IMAGE=image, TITLE=title) + if image_html == '': + logger.warning('None index.html found, --gen-main paused.') + return try: data = main.format(STYLES=css, COUNT=count, PICTURE=image_html) if sys.version_info < (3, 0): @@ -133,7 +138,7 @@ def generate_main_html(output_dir='.'): else: with open('./main.html', 'wb') as f: f.write(data.encode('utf-8')) - logger.log(15, 'Main Viewer has been write to \'{0}\''.format('./main.html')) + logger.log(15, 'Main Viewer has been write to \'{0}/main.html\''.format(output_dir)) except Exception as e: logger.warning('Writen Main Viewer failed ({})'.format(str(e))) logger.info('==>Process finished.') From f10ae3cf58bc4dc486cdb3efe04850257581eac2 Mon Sep 17 00:00:00 2001 From: symant233 Date: Tue, 28 May 2019 19:47:48 +0800 Subject: [PATCH 11/21] store proxy config --- nhentai/cmdline.py | 37 ++++++++++++++++++++++++------------- nhentai/command.py | 3 ++- nhentai/utils.py | 19 +++++++++---------- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/nhentai/cmdline.py b/nhentai/cmdline.py index de6455c..9e15359 100644 --- a/nhentai/cmdline.py +++ b/nhentai/cmdline.py @@ -69,7 +69,7 @@ def cmd_parser(): parser.add_option('--delay', '-d', type='int', dest='delay', action='store', default=0, help='slow down between downloading every doujinshi') parser.add_option('--proxy', '-p', type='string', dest='proxy', action='store', default='', - help='uses a proxy, for example: http://127.0.0.1:1080') + help='store a proxy, for example: -p \'http://127.0.0.1:1080\'') parser.add_option('--file', '-f', type='string', dest='file', action='store', help='read gallery IDs from file.') parser.add_option('--format', type='string', dest='name_format', action='store', help='format the saved folder name', default='[%i][%a][%t]') @@ -125,6 +125,29 @@ def cmd_parser(): logger.info('Cookie saved.') exit(0) + if os.path.exists(os.path.join(constant.NHENTAI_HOME, 'proxy')): + with open(os.path.join(constant.NHENTAI_HOME, 'proxy'), 'r') as f: + link = f.read() + constant.PROXY = {'http': link, 'https': link} + + if args.proxy: + try: + if not os.path.exists(constant.NHENTAI_HOME): + os.mkdir(constant.NHENTAI_HOME) + + 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: + with open(os.path.join(constant.NHENTAI_HOME, 'proxy'), 'w') as f: + f.write(args.proxy) + except Exception as e: + logger.error('Cannot create NHENTAI_HOME: {}'.format(str(e))) + exit(1) + + logger.info('Proxy \'{0}\' saved.'.format(args.proxy)) + exit(0) + ''' if args.login: try: @@ -168,16 +191,4 @@ def cmd_parser(): logger.critical('Maximum number of used threads is 15') exit(1) - if args.proxy: - proxy_url = urlparse(args.proxy) - if args.proxy == 'default' or 'd': - constant.PROXY = { - 'http': "http://127.0.0.1:1080", - 'https': "http://127.0.0.1:1080" - } - elif 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 71d0669..88a8c97 100644 --- a/nhentai/command.py +++ b/nhentai/command.py @@ -61,7 +61,8 @@ def main(): generate_html(options.output_dir, doujinshi) elif options.is_cbz: generate_cbz(options.output_dir, doujinshi, options.rm_origin_dir) - generate_main_html(options.output_dir) + if options.main_viewer: + generate_main_html(options.output_dir) if not platform.system() == 'Windows': logger.log(15, '🍻 All done.') else: diff --git a/nhentai/utils.py b/nhentai/utils.py index ce61aa9..61c535e 100644 --- a/nhentai/utils.py +++ b/nhentai/utils.py @@ -84,8 +84,7 @@ def generate_html(output_dir='.', doujinshi_obj=None): def generate_main_html(output_dir='.'): """Generete a main html to show all the contain doujinshi. With a link to thier `index.html`. - Default output folder will be the CLI path. - """ + Default output folder will be the CLI path.""" count = 0 image_html = '' main = readfile('viewer/main.html') @@ -99,13 +98,13 @@ def generate_main_html(output_dir='.'): \n\
\n\
\n' - + if output_dir == '': os.chdir('.') else: - os.chdir(output_dir) + os.chdir(output_dir) # switch to given dir - doujinshi_dirs = next(os.walk('.'))[1] + doujinshi_dirs = next(os.walk('.'))[1] # https://stackoverflow.com/questions/141291/how-to-list-only-top-level-directories-in-python for folder in doujinshi_dirs: @@ -115,10 +114,10 @@ def generate_main_html(output_dir='.'): if 'index.html' in files: count += 1 else: - logger.warning('{} folder does not have index.html, try use --html arg first.'\ - .format(folder)) + logger.warning('{} folder does not have index.html (try use --html arg first).' + .format(folder)) continue - image = files[0] # 001.jpg or 001.png + image = files[0] # 001.jpg or 001.png if folder is not None: title = folder.replace('_', ' ') # if sys.version_info > (3, 0): @@ -138,10 +137,10 @@ def generate_main_html(output_dir='.'): else: with open('./main.html', 'wb') as f: f.write(data.encode('utf-8')) - logger.log(15, 'Main Viewer has been write to \'{0}/main.html\''.format(output_dir)) + logger.log( + 15, 'Main Viewer has been write to \'{0}/main.html\''.format(output_dir)) except Exception as e: logger.warning('Writen Main Viewer failed ({})'.format(str(e))) - logger.info('==>Process finished.') def generate_cbz(output_dir='.', doujinshi_obj=None, rm_origin_dir=False): if doujinshi_obj is not None: From 0935d609c353f472c6cc024655f1e383595a5817 Mon Sep 17 00:00:00 2001 From: symant233 Date: Wed, 29 May 2019 13:43:47 +0800 Subject: [PATCH 12/21] fix --gen-main action --- nhentai/cmdline.py | 2 +- nhentai/command.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/nhentai/cmdline.py b/nhentai/cmdline.py index 9e15359..2b87250 100644 --- a/nhentai/cmdline.py +++ b/nhentai/cmdline.py @@ -103,7 +103,7 @@ def cmd_parser(): generate_html() exit(0) - if args.main_viewer: + if args.main_viewer and not args.favorites and args.id is None and not args.is_download: generate_main_html() exit(0) diff --git a/nhentai/command.py b/nhentai/command.py index 88a8c97..907ec29 100644 --- a/nhentai/command.py +++ b/nhentai/command.py @@ -16,8 +16,13 @@ from nhentai.utils import generate_html, generate_cbz, generate_main_html def main(): banner() - logger.info('Using mirror: {0}'.format(BASE_URL)) options = cmd_parser() + logger.info('Using mirror: {0}'.format(BASE_URL)) + + from nhentai.constant import PROXY + # constant.PROXY will be changed after cmd_parser() + if PROXY != {}: + logger.info('Using proxy: {0}'.format(PROXY)) doujinshi_ids = [] doujinshi_list = [] From 9b6554494265cb1daff75bc4a6fbde8bd6a0bccf Mon Sep 17 00:00:00 2001 From: symant233 Date: Thu, 30 May 2019 20:05:46 +0800 Subject: [PATCH 13/21] add travis-ci test --- .travis.yml | 1 + nhentai/utils.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b0b5eef..1e49f11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,3 +19,4 @@ script: - NHENTAI=https://nhentai.net nhentai --tag lolicon - NHENTAI=https://nhentai.net nhentai -F - NHENTAI=https://nhentai.net nhentai --file /tmp/test.txt + - nhentai --id=152503,146134 --gen-main --output=/tmp/ diff --git a/nhentai/utils.py b/nhentai/utils.py index 61c535e..9cafe37 100644 --- a/nhentai/utils.py +++ b/nhentai/utils.py @@ -138,7 +138,7 @@ def generate_main_html(output_dir='.'): with open('./main.html', 'wb') as f: f.write(data.encode('utf-8')) 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: logger.warning('Writen Main Viewer failed ({})'.format(str(e))) From f5b7d89fb043da0c6271247b31dd1f98ee8c07b2 Mon Sep 17 00:00:00 2001 From: symant233 Date: Sat, 1 Jun 2019 11:31:53 +0800 Subject: [PATCH 14/21] fix show info --- nhentai/cmdline.py | 3 ++- nhentai/doujinshi.py | 4 ++-- nhentai/parser.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/nhentai/cmdline.py b/nhentai/cmdline.py index 2b87250..b0f6509 100644 --- a/nhentai/cmdline.py +++ b/nhentai/cmdline.py @@ -103,7 +103,8 @@ def cmd_parser(): generate_html() exit(0) - if args.main_viewer and not args.favorites and args.id is None and not args.is_download: + if args.main_viewer and not args.id and not args.keyword and \ + not args.tag and not args.favorites: generate_main_html() exit(0) diff --git a/nhentai/doujinshi.py b/nhentai/doujinshi.py index 0ee0598..9f82c32 100644 --- a/nhentai/doujinshi.py +++ b/nhentai/doujinshi.py @@ -50,9 +50,9 @@ class Doujinshi(object): table = [ ["Doujinshi", self.name], ["Subtitle", self.info.subtitle], - ["Characters", self.info.character], + ["Characters", self.info.characters], ["Authors", self.info.artists], - ["Language", self.info.language], + ["Languages", self.info.languages], ["Tags", self.info.tags], ["URL", self.url], ["Pages", self.pages], diff --git a/nhentai/parser.py b/nhentai/parser.py index 34555e0..ae628d6 100644 --- a/nhentai/parser.py +++ b/nhentai/parser.py @@ -162,7 +162,7 @@ def doujinshi_parser(id_): # gain information of the doujinshi information_fields = doujinshi_info.find_all('div', attrs={'class': 'field-name'}) - needed_fields = ['Characters', 'Artists', 'Language', 'Tags'] + needed_fields = ['Characters', 'Artists', 'Languages', 'Tags'] for field in information_fields: field_name = field.contents[0].strip().strip(':') if field_name in needed_fields: From a909ad6d9226c15e338c15dfa2a8d060c696e76c Mon Sep 17 00:00:00 2001 From: RicterZ Date: Tue, 4 Jun 2019 08:35:13 +0800 Subject: [PATCH 15/21] fix --gen-main bugs --- nhentai/utils.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/nhentai/utils.py b/nhentai/utils.py index 9cafe37..260edb1 100644 --- a/nhentai/utils.py +++ b/nhentai/utils.py @@ -81,10 +81,14 @@ def generate_html(output_dir='.', doujinshi_obj=None): except Exception as e: logger.warning('Writen HTML Viewer failed ({})'.format(str(e))) -def generate_main_html(output_dir='.'): - """Generete a main html to show all the contain doujinshi. - With a link to thier `index.html`. - Default output folder will be the CLI path.""" + +def generate_main_html(output_dir='./'): + """ + Generate a main html to show all the contain doujinshi. + With a link to their `index.html`. + Default output folder will be the CLI path. + """ + count = 0 image_html = '' main = readfile('viewer/main.html') @@ -99,31 +103,26 @@ def generate_main_html(output_dir='.'): \n\ \n' - if output_dir == '': - os.chdir('.') - else: - os.chdir(output_dir) - # switch to given dir + os.chdir(output_dir) doujinshi_dirs = next(os.walk('.'))[1] - # https://stackoverflow.com/questions/141291/how-to-list-only-top-level-directories-in-python for folder in doujinshi_dirs: - if folder[0] is not '[': - continue + files = os.listdir(folder) + files.sort() + if 'index.html' in files: count += 1 + logger.info('Add doujinshi \'{}\''.format(folder)) else: - logger.warning('{} folder does not have index.html (try use --html arg first).' - .format(folder)) continue + image = files[0] # 001.jpg or 001.png if folder is not None: title = folder.replace('_', ' ') - # if sys.version_info > (3, 0): - # title = title.encode('utf-8') else: title = 'nHentai HTML Viewer' + image_html += element.format(FOLDER=folder, IMAGE=image, TITLE=title) if image_html == '': @@ -142,6 +141,7 @@ def generate_main_html(output_dir='.'): except Exception as e: logger.warning('Writen Main Viewer failed ({})'.format(str(e))) + def generate_cbz(output_dir='.', doujinshi_obj=None, rm_origin_dir=False): if doujinshi_obj is not None: doujinshi_dir = os.path.join(output_dir, doujinshi_obj.filename) From 11722823622aac8bb695a37e695fec9db7366cc9 Mon Sep 17 00:00:00 2001 From: RicterZ Date: Tue, 4 Jun 2019 08:38:42 +0800 Subject: [PATCH 16/21] fix #50 --- nhentai/doujinshi.py | 2 +- nhentai/utils.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/nhentai/doujinshi.py b/nhentai/doujinshi.py index 9f82c32..a36038c 100644 --- a/nhentai/doujinshi.py +++ b/nhentai/doujinshi.py @@ -41,7 +41,7 @@ class Doujinshi(object): name_format = name_format.replace('%a', self.info.artists) name_format = name_format.replace('%t', self.name) name_format = name_format.replace('%s', self.info.subtitle) - self.filename = name_format + self.filename = format_filename(name_format) def __repr__(self): return ''.format(self.name) diff --git a/nhentai/utils.py b/nhentai/utils.py index 260edb1..c089f25 100644 --- a/nhentai/utils.py +++ b/nhentai/utils.py @@ -178,7 +178,6 @@ 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. if len(filename) > 100: filename = filename[:100] + '...]' From 0cfec34e9ebcb771911007b478c4732e8a4f2477 Mon Sep 17 00:00:00 2001 From: RicterZ Date: Wed, 12 Jun 2019 22:08:32 +0800 Subject: [PATCH 17/21] modify cookie --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1e49f11..6b5330a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ install: script: - echo 268642 > /tmp/test.txt - - NHENTAI=https://nhentai.net nhentai --cookie '__cfduid=da09f237ceb0f51c75980b0b3fda3ce571558179357; _ga=GA1.2.2000087053.1558179358; _gid=GA1.2.717818542.1558179358; csrftoken=iSxrTFOjrujJqauhAqWvTTI9dl3sfWnxdEFoMuqgmlBrbMin5Gj9wJW4r61cmH1X; sessionid=ewuaayfewbzpiukrarx9d52oxwlz2esd' + - NHENTAI=https://nhentai.net nhentai --cookie '__cfduid=da09f237ceb0f51c75980b0b3fda3ce571558179357; _ga=GA1.2.2000087053.1558179358; _gid=GA1.2.782652201.1560348447; csrftoken=E2O8wfriFkcXUgN1AC41DoLqfRaBbggIUdvy46yC45PKCRCmCHQHQ7YRUy0d7FXZ; sessionid=0rapzxkt6yl1wjhdxm9whtfdc7gvw0iu' - NHENTAI=https://nhentai.net nhentai --search umaru - NHENTAI=https://nhentai.net nhentai --id=152503,146134 -t 10 --output=/tmp/ --cbz - NHENTAI=https://nhentai.net nhentai --tag lolicon From 58b2b644c17d0aa4ae3a207827632d41ef89aafe Mon Sep 17 00:00:00 2001 From: RicterZ Date: Wed, 12 Jun 2019 22:37:25 +0800 Subject: [PATCH 18/21] fix #64 --- nhentai/cmdline.py | 12 --- nhentai/command.py | 5 +- nhentai/parser.py | 223 +++++++++++++++++++++------------------------ nhentai/utils.py | 13 +++ 4 files changed, 119 insertions(+), 134 deletions(-) diff --git a/nhentai/cmdline.py b/nhentai/cmdline.py index b0f6509..2a58736 100644 --- a/nhentai/cmdline.py +++ b/nhentai/cmdline.py @@ -149,18 +149,6 @@ def cmd_parser(): logger.info('Proxy \'{0}\' saved.'.format(args.proxy)) 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.favorites: if not constant.COOKIE: logger.warning('Cookie has not been set, please use `nhentai --cookie \'COOKIE\'` to set it.') diff --git a/nhentai/command.py b/nhentai/command.py index 907ec29..d45d20f 100644 --- a/nhentai/command.py +++ b/nhentai/command.py @@ -31,7 +31,10 @@ def main(): if not options.is_download: logger.warning('You do not specify --download option') - doujinshi_ids = favorites_parser() + doujinshis = favorites_parser() + print_doujinshi(doujinshis) + if options.is_download and doujinshis: + doujinshi_ids = map(lambda d: d['id'], doujinshis) elif options.tag: doujinshis = tag_parser(options.tag, max_page=options.max_page) diff --git a/nhentai/parser.py b/nhentai/parser.py index ae628d6..b96e707 100644 --- a/nhentai/parser.py +++ b/nhentai/parser.py @@ -10,25 +10,10 @@ from bs4 import BeautifulSoup from tabulate import tabulate import nhentai.constant as constant +from nhentai.utils import request from nhentai.logger import logger -session = requests.Session() -session.headers.update({ - 'Referer': constant.LOGIN_URL, - 'User-Agent': 'nhentai command line client (https://github.com/RicterZ/nhentai)', -}) - - -def request(method, url, **kwargs): - global session - if not hasattr(session, method): - raise AttributeError('\'requests.Session\' object has no attribute \'{0}\''.format(method)) - - session.headers.update({'Cookie': constant.COOKIE}) - return getattr(session, method)(url, proxies=constant.PROXY, verify=False, **kwargs) - - def _get_csrf_token(content): html = BeautifulSoup(content, 'html.parser') csrf_token_elem = html.find('input', attrs={'name': 'csrfmiddlewaretoken'}) @@ -66,7 +51,22 @@ def login(username, password): exit(2) +def _get_title_and_id(response): + result = [] + html = BeautifulSoup(response, 'html.parser') + doujinshi_search_result = html.find_all('div', attrs={'class': 'gallery'}) + for doujinshi in doujinshi_search_result: + doujinshi_container = doujinshi.find('div', attrs={'class': 'caption'}) + title = doujinshi_container.text.strip() + title = title if len(title) < 85 else title[:82] + '...' + id_ = re.search('/g/(\d+)/', doujinshi.a['href']).group(1) + result.append({'id': id_, 'title': title}) + + return result + + def favorites_parser(): + result = [] html = BeautifulSoup(request('get', constant.FAV_URL).content, 'html.parser') count = html.find('span', attrs={'class': 'count'}) if not count: @@ -89,20 +89,16 @@ def favorites_parser(): if os.getenv('DEBUG'): pages = 1 - ret = [] - doujinshi_id = re.compile('data-id="([\d]+)"') - for page in range(1, pages + 1): try: logger.info('Getting doujinshi ids of page %d' % page) - resp = request('get', constant.FAV_URL + '?page=%d' % page).text - ids = doujinshi_id.findall(resp) - ret.extend(ids) + resp = request('get', constant.FAV_URL + '?page=%d' % page).content + result.extend(_get_title_and_id(resp)) except Exception as e: logger.error('Error: %s, continue', str(e)) - return ret + return result def doujinshi_parser(id_): @@ -175,7 +171,6 @@ def doujinshi_parser(id_): def search_parser(keyword, page): logger.debug('Searching doujinshis of keyword {0}'.format(keyword)) - result = [] try: response = request('get', url=constant.SEARCH_URL, params={'q': keyword, 'page': page}).content except requests.ConnectionError as e: @@ -183,20 +178,95 @@ def search_parser(keyword, page): logger.warn('If you are in China, please configure the proxy to fu*k GFW.') raise SystemExit - html = BeautifulSoup(response, 'html.parser') - doujinshi_search_result = html.find_all('div', attrs={'class': 'gallery'}) - for doujinshi in doujinshi_search_result: - doujinshi_container = doujinshi.find('div', attrs={'class': 'caption'}) - title = doujinshi_container.text.strip() - title = title if len(title) < 85 else title[:82] + '...' - id_ = re.search('/g/(\d+)/', doujinshi.a['href']).group(1) - result.append({'id': id_, 'title': title}) + result = _get_title_and_id(response) 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')) + + +def tag_parser(tag_name, max_page=1): + result = [] + tag_name = tag_name.lower() + tag_name = tag_name.replace(' ', '-') + + for p in range(1, max_page + 1): + logger.debug('Fetching page {0} for doujinshi with tag \'{1}\''.format(p, tag_name)) + response = request('get', url='%s/%s?page=%d' % (constant.TAG_URL, tag_name, p)).content + + result = _get_title_and_id(response) + if not result: + logger.error('Cannot find doujinshi id of tag \'{0}\''.format(tag_name)) + return + + if not result: + logger.warn('No results for tag \'{}\''.format(tag_name)) + + return result + + +def __api_suspended_search_parser(keyword, page): + logger.debug('Searching doujinshis using keywords {0}'.format(keyword)) + result = [] + i = 0 + while i < 5: + try: + response = request('get', url=constant.SEARCH_URL, params={'query': keyword, 'page': page}).json() + except Exception as e: + i += 1 + if not i < 5: + logger.critical(str(e)) + logger.warn('If you are in China, please configure the proxy to fu*k GFW.') + exit(1) + continue + break + + if 'result' not in response: + raise Exception('No result in response') + + 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('No results for keywords {}'.format(keyword)) + + return result + + +def __api_suspended_tag_parser(tag_id, max_page=1): + logger.info('Searching for doujinshi with tag id {0}'.format(tag_id)) + result = [] + response = request('get', url=constant.TAG_API_URL, params={'sort': 'popular', 'tag_id': tag_id}).json() + page = max_page if max_page <= response['num_pages'] else int(response['num_pages']) + + for i in range(1, page + 1): + logger.info('Getting page {} ...'.format(i)) + + if page != 1: + response = request('get', url=constant.TAG_API_URL, + params={'sort': 'popular', 'tag_id': tag_id}).json() + 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('No results for tag id {}'.format(tag_id)) + + return result + + def __api_suspended_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_)) @@ -244,94 +314,5 @@ def __api_suspended_doujinshi_parser(id_): return doujinshi -def __api_suspended_search_parser(keyword, page): - logger.debug('Searching doujinshis using keywords {0}'.format(keyword)) - result = [] - i = 0 - while i < 5: - try: - response = request('get', url=constant.SEARCH_URL, params={'query': keyword, 'page': page}).json() - except Exception as e: - i += 1 - if not i < 5: - logger.critical(str(e)) - logger.warn('If you are in China, please configure the proxy to fu*k GFW.') - exit(1) - continue - break - - if 'result' not in response: - raise Exception('No result in response') - - 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('No results for keywords {}'.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')) - - -def __api_suspended_tag_parser(tag_id, max_page=1): - logger.info('Searching for doujinshi with tag id {0}'.format(tag_id)) - result = [] - response = request('get', url=constant.TAG_API_URL, params={'sort': 'popular', 'tag_id': tag_id}).json() - page = max_page if max_page <= response['num_pages'] else int(response['num_pages']) - - for i in range(1, page + 1): - logger.info('Getting page {} ...'.format(i)) - - if page != 1: - response = request('get', url=constant.TAG_API_URL, - params={'sort': 'popular', 'tag_id': tag_id}).json() - 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('No results for tag id {}'.format(tag_id)) - - return result - - -def tag_parser(tag_name, max_page=1): - result = [] - tag_name = tag_name.lower() - tag_name = tag_name.replace(' ', '-') - - for p in range(1, max_page + 1): - logger.debug('Fetching page {0} for doujinshi with tag \'{1}\''.format(p, tag_name)) - response = request('get', url='%s/%s?page=%d' % (constant.TAG_URL, tag_name, p)).content - - html = BeautifulSoup(response, 'html.parser') - doujinshi_items = html.find_all('div', attrs={'class': 'gallery'}) - if not doujinshi_items: - logger.error('Cannot find doujinshi id of tag \'{0}\''.format(tag_name)) - return - - for i in doujinshi_items: - doujinshi_id = i.a.attrs['href'].strip('/g') - doujinshi_title = i.a.text.strip() - doujinshi_title = doujinshi_title if len(doujinshi_title) < 85 else doujinshi_title[:82] + '...' - result.append({'title': doujinshi_title, 'id': doujinshi_id}) - - if not result: - logger.warn('No results for tag \'{}\''.format(tag_name)) - - return result - - if __name__ == '__main__': print(doujinshi_parser("32271")) diff --git a/nhentai/utils.py b/nhentai/utils.py index c089f25..63db14c 100644 --- a/nhentai/utils.py +++ b/nhentai/utils.py @@ -6,9 +6,22 @@ import os import string import zipfile import shutil +import requests + +from nhentai import constant from nhentai.logger import logger +def request(method, url, **kwargs): + session = requests.Session() + session.headers.update({ + 'Referer': constant.LOGIN_URL, + 'User-Agent': 'nhentai command line client (https://github.com/RicterZ/nhentai)', + 'Cookie': constant.COOKIE + }) + return getattr(session, method)(url, proxies=constant.PROXY, verify=False, **kwargs) + + class _Singleton(type): """ A metaclass that creates a Singleton base class when called. """ _instances = {} From 1af195d7274c91e7437420d0cecfb7eca6b0db82 Mon Sep 17 00:00:00 2001 From: RicterZ Date: Wed, 12 Jun 2019 22:45:44 +0800 Subject: [PATCH 19/21] add cookie check --- nhentai/command.py | 5 ++++- nhentai/utils.py | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/nhentai/command.py b/nhentai/command.py index d45d20f..10176ea 100644 --- a/nhentai/command.py +++ b/nhentai/command.py @@ -11,7 +11,7 @@ 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, generate_main_html +from nhentai.utils import generate_html, generate_cbz, generate_main_html, check_cookie def main(): @@ -24,6 +24,9 @@ def main(): if PROXY != {}: logger.info('Using proxy: {0}'.format(PROXY)) + # check your cookie + check_cookie() + doujinshi_ids = [] doujinshi_list = [] diff --git a/nhentai/utils.py b/nhentai/utils.py index 63db14c..3bdf278 100644 --- a/nhentai/utils.py +++ b/nhentai/utils.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals, print_function import sys +import re import os import string import zipfile @@ -22,6 +23,15 @@ def request(method, url, **kwargs): return getattr(session, method)(url, proxies=constant.PROXY, verify=False, **kwargs) +def check_cookie(): + response = request('get', constant.BASE_URL).text + username = re.findall('"/users/\d+/(.*?)"', response) + if not username: + logger.error('Cannot get your username, please check your cookie or use `nhentai --cookie` to set your cookie') + else: + logger.info('Login successfully! Your username: {}'.format(username[0])) + + class _Singleton(type): """ A metaclass that creates a Singleton base class when called. """ _instances = {} From 6b97777b7d782fba1e076331bebe24c710172dd2 Mon Sep 17 00:00:00 2001 From: RicterZ Date: Wed, 12 Jun 2019 22:48:41 +0800 Subject: [PATCH 20/21] fix bug --- nhentai/constant.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nhentai/constant.py b/nhentai/constant.py index 11c0d02..121266b 100644 --- a/nhentai/constant.py +++ b/nhentai/constant.py @@ -2,7 +2,12 @@ from __future__ import unicode_literals, print_function import os import tempfile -from nhentai.utils import urlparse + +try: + from urlparse import urlparse +except ImportError: + from urllib.parse import urlparse + BASE_URL = os.getenv('NHENTAI', 'https://nhentai.net') From 92640d97678423d28ef1079bab42e304eb9c9c60 Mon Sep 17 00:00:00 2001 From: RicterZ Date: Wed, 12 Jun 2019 22:54:22 +0800 Subject: [PATCH 21/21] 0.3.5 --- nhentai/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nhentai/__init__.py b/nhentai/__init__.py index 42ea186..8efd062 100644 --- a/nhentai/__init__.py +++ b/nhentai/__init__.py @@ -1,3 +1,3 @@ -__version__ = '0.3.4' +__version__ = '0.3.5' __author__ = 'RicterZ' __email__ = 'ricterzheng@gmail.com'