Compare commits

..

7 Commits

Author SHA1 Message Date
14b3c82248 remove \r 2018-08-11 09:28:39 +08:00
4577e9df9a fix 2018-08-11 09:24:16 +08:00
de157ccb7f Merge branch 'master' of github.com:RicterZ/nhentai 2018-08-11 09:19:31 +08:00
126bbe8d49 add a test 2018-08-11 09:18:00 +08:00
8546b9e759 fix bug #24 2018-08-11 09:17:05 +08:00
6ff9751c30 fix 2018-07-01 12:50:37 +08:00
f316c3243b 0.2.12 2018-04-19 17:29:23 +08:00
9 changed files with 244 additions and 236 deletions

View File

@ -5,9 +5,8 @@ language: python
python: python:
- 2.7 - 2.7
- 2.6 - 2.6
- 3.3 - 3.6
- 3.4 - 3.7
- 3.5.2
install: install:
- python setup.py install - python setup.py install
@ -15,3 +14,4 @@ install:
script: script:
- NHENTAI=https://nhentai.net nhentai --search umaru - NHENTAI=https://nhentai.net nhentai --search umaru
- NHENTAI=https://nhentai.net nhentai --id=152503,146134 -t 10 --output=/tmp/ - NHENTAI=https://nhentai.net nhentai --id=152503,146134 -t 10 --output=/tmp/
- NHENTAI=https://nhentai.net nhentai -l nhentai_test:nhentai --output=/tmp/

View File

@ -1,5 +1,5 @@
include README.md include README.md
include requirements.txt include requirements.txt
include nhentai/viewer/index.html include nhentai/viewer/index.html
include nhentai/viewer/styles.css include nhentai/viewer/styles.css
include nhentai/viewer/scripts.js include nhentai/viewer/scripts.js

138
README.md
View File

@ -1,70 +1,70 @@
nhentai nhentai
======= =======
_ _ _ _ _ _ _ _
_ __ | | | | ___ _ __ | |_ __ _(_) _ __ | | | | ___ _ __ | |_ __ _(_)
| '_ \| |_| |/ _ \ '_ \| __/ _` | | | '_ \| |_| |/ _ \ '_ \| __/ _` | |
| | | | _ | __/ | | | || (_| | | | | | | _ | __/ | | | || (_| | |
|_| |_|_| |_|\___|_| |_|\__\__,_|_| |_| |_|_| |_|\___|_| |_|\__\__,_|_|
あなたも変態。 いいね? あなたも変態。 いいね?
[![Build Status](https://travis-ci.org/RicterZ/nhentai.svg?branch=master)](https://travis-ci.org/RicterZ/nhentai) [![Build Status](https://travis-ci.org/RicterZ/nhentai.svg?branch=master)](https://travis-ci.org/RicterZ/nhentai)
🎉🎉 nhentai 现在支持 Windows 啦! 🎉🎉 nhentai 现在支持 Windows 啦!
由于 [http://nhentai.net](http://nhentai.net) 下载下来的种子速度很慢,而且官方也提供在线观看本子的功能,所以可以利用本脚本下载本子。 由于 [http://nhentai.net](http://nhentai.net) 下载下来的种子速度很慢,而且官方也提供在线观看本子的功能,所以可以利用本脚本下载本子。
### Installation ### Installation
git clone https://github.com/RicterZ/nhentai git clone https://github.com/RicterZ/nhentai
cd nhentai cd nhentai
python setup.py install python setup.py install
### Gentoo ### Gentoo
layman -fa glicOne layman -fa glicOne
sudo emerge net-misc/nhentai sudo emerge net-misc/nhentai
### Usage ### Usage
下载指定 id 列表的本子: 下载指定 id 列表的本子:
```bash ```bash
nhentai --id=123855,123866 nhentai --id=123855,123866
``` ```
下载某关键词第一页的本子: 下载某关键词第一页的本子:
```bash ```bash
nhentai --search="tomori" --page=1 --download nhentai --search="tomori" --page=1 --download
``` ```
下载用户 favorites 内容: 下载用户 favorites 内容:
```bash ```bash
nhentai --login "username:password" --download nhentai --login "username:password" --download
``` ```
### Options ### Options
`-t, --thread`:指定下载的线程数,最多为 10 线程。 `-t, --thread`:指定下载的线程数,最多为 10 线程。
`--path`:指定下载文件的输出路径,默认为当前目录。 `--path`:指定下载文件的输出路径,默认为当前目录。
`--timeout`:指定下载图片的超时时间,默认为 30 秒。 `--timeout`:指定下载图片的超时时间,默认为 30 秒。
`--proxy`:指定下载的代理,例如: http://127.0.0.1:8080/ `--proxy`:指定下载的代理,例如: http://127.0.0.1:8080/
`--login`nhentai 账号的“用户名:密码”组合 `--login`nhentai 账号的“用户名:密码”组合
`--nohtml`nhentai Don't generate HTML `--nohtml`nhentai Don't generate HTML
`--cbz`nhentai Generate Comic Book CBZ file `--cbz`nhentai Generate Comic Book CBZ file
### nHentai Mirror ### nHentai Mirror
如果想用自建镜像下载 nhentai 的本子,需要搭建 nhentai.net 和 i.nhentai.net 的反向代理。 如果想用自建镜像下载 nhentai 的本子,需要搭建 nhentai.net 和 i.nhentai.net 的反向代理。
例如用 h.loli.club 来做反向代理的话,需要 h.loli.club 反代 nhentai.neti.h.loli.club 反带 i.nhentai.net。 例如用 h.loli.club 来做反向代理的话,需要 h.loli.club 反代 nhentai.neti.h.loli.club 反带 i.nhentai.net。
然后利用环境变量来下载: 然后利用环境变量来下载:
```bash ```bash
NHENTAI=http://h.loli.club nhentai --id 123456 NHENTAI=http://h.loli.club nhentai --id 123456
``` ```
![](./images/search.png) ![](./images/search.png)
![](./images/download.png) ![](./images/download.png)
![](./images/viewer.png) ![](./images/viewer.png)
### License ### License
MIT MIT
### あなたも変態 ### あなたも変態
![](./images/image.jpg) ![](./images/image.jpg)

View File

@ -1,3 +1,3 @@
__version__ = '0.2.12' __version__ = '0.2.14'
__author__ = 'RicterZ' __author__ = 'RicterZ'
__email__ = 'ricterzheng@gmail.com' __email__ = 'ricterzheng@gmail.com'

View File

@ -27,7 +27,7 @@ def login_parser(username, password):
s.get(constant.LOGIN_URL) s.get(constant.LOGIN_URL)
content = s.get(constant.LOGIN_URL).content content = s.get(constant.LOGIN_URL).content
html = BeautifulSoup(content, 'html.parser').encode("ascii") html = BeautifulSoup(content, 'html.parser')
csrf_token_elem = html.find('input', attrs={'name': 'csrfmiddlewaretoken'}) csrf_token_elem = html.find('input', attrs={'name': 'csrfmiddlewaretoken'})
if not csrf_token_elem: if not csrf_token_elem:
@ -44,14 +44,22 @@ def login_parser(username, password):
logger.error('Login failed, please check your username and password') logger.error('Login failed, please check your username and password')
exit(1) exit(1)
html = BeautifulSoup(s.get(constant.FAV_URL).content, 'html.parser').encode("ascii") html = BeautifulSoup(s.get(constant.FAV_URL).content, 'html.parser')
count = html.find('span', attrs={'class': 'count'}) count = html.find('span', attrs={'class': 'count'})
if not count: if not count:
logger.error('Cannot get count of your favorites, maybe login failed.') logger.error('Cannot get count of your favorites, maybe login failed.')
count = int(count.text.strip('(').strip(')')) count = int(count.text.strip('(').strip(')'))
pages = count / 25 if count == 0:
pages += 1 if count % (25 * pages) else 0 logger.warning('No favorites found')
return []
pages = int(count / 25)
if pages:
pages += 1 if count % (25 * pages) else 0
else:
pages = 1
logger.info('Your have %d favorites in %d pages.' % (count, pages)) logger.info('Your have %d favorites in %d pages.' % (count, pages))
if os.getenv('DEBUG'): if os.getenv('DEBUG'):
@ -68,7 +76,7 @@ def login_parser(username, password):
for page in range(1, pages+1): for page in range(1, pages+1):
try: try:
logger.info('Getting doujinshi id of page %d' % page) logger.info('Getting doujinshi id of page %d' % page)
resp = s.get(constant.FAV_URL + '?page=%d' % page).content resp = s.get(constant.FAV_URL + '?page=%d' % page).text
ids = doujinshi_id.findall(resp) ids = doujinshi_id.findall(resp)
requests_ = threadpool.makeRequests(doujinshi_parser, ids, _callback) requests_ = threadpool.makeRequests(doujinshi_parser, ids, _callback)
[thread_pool.putRequest(req) for req in requests_] [thread_pool.putRequest(req) for req in requests_]

View File

@ -1,24 +1,24 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>{TITLE}</title> <title>{TITLE}</title>
<style> <style>
{STYLES} {STYLES}
</style> </style>
</head> </head>
<body> <body>
<nav id="list"> <nav id="list">
{IMAGES}</nav> {IMAGES}</nav>
<div id="image-container"> <div id="image-container">
<span id="page-num"></span> <span id="page-num"></span>
<div id="dest"></div> <div id="dest"></div>
</div> </div>
<script> <script>
{SCRIPTS} {SCRIPTS}
</script> </script>
</body> </body>
</html> </html>

View File

@ -1,62 +1,62 @@
const pages = Array.from(document.querySelectorAll('img.image-item')); const pages = Array.from(document.querySelectorAll('img.image-item'));
let currentPage = 0; let currentPage = 0;
function changePage(pageNum) { function changePage(pageNum) {
const previous = pages[currentPage]; const previous = pages[currentPage];
const current = pages[pageNum]; const current = pages[pageNum];
if (current == null) { if (current == null) {
return; return;
} }
previous.classList.remove('current'); previous.classList.remove('current');
current.classList.add('current'); current.classList.add('current');
currentPage = pageNum; currentPage = pageNum;
const display = document.getElementById('dest'); const display = document.getElementById('dest');
display.style.backgroundImage = `url("${current.src}")`; display.style.backgroundImage = `url("${current.src}")`;
document.getElementById('page-num') document.getElementById('page-num')
.innerText = [ .innerText = [
(pageNum + 1).toLocaleString(), (pageNum + 1).toLocaleString(),
pages.length.toLocaleString() pages.length.toLocaleString()
].join('\u200a/\u200a'); ].join('\u200a/\u200a');
} }
changePage(0); changePage(0);
document.getElementById('list').onclick = event => { document.getElementById('list').onclick = event => {
if (pages.includes(event.target)) { if (pages.includes(event.target)) {
changePage(pages.indexOf(event.target)); changePage(pages.indexOf(event.target));
} }
}; };
document.getElementById('image-container').onclick = event => { document.getElementById('image-container').onclick = event => {
const width = document.getElementById('image-container').clientWidth; const width = document.getElementById('image-container').clientWidth;
const clickPos = event.clientX / width; const clickPos = event.clientX / width;
if (clickPos < 0.5) { if (clickPos < 0.5) {
changePage(currentPage - 1); changePage(currentPage - 1);
} else { } else {
changePage(currentPage + 1); changePage(currentPage + 1);
} }
}; };
document.onkeypress = event => { document.onkeypress = event => {
switch (event.key.toLowerCase()) { switch (event.key.toLowerCase()) {
// Previous Image // Previous Image
case 'arrowleft': case 'arrowleft':
case 'a': case 'a':
changePage(currentPage - 1); changePage(currentPage - 1);
break; break;
// Next Image // Next Image
case ' ': case ' ':
case 'enter': case 'enter':
case 'arrowright': case 'arrowright':
case 'd': case 'd':
changePage(currentPage + 1); changePage(currentPage + 1);
break; break;
} }
}; };

View File

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

View File

@ -2,4 +2,4 @@ requests>=2.5.0
BeautifulSoup4>=4.0.0 BeautifulSoup4>=4.0.0
threadpool>=1.2.7 threadpool>=1.2.7
tabulate>=0.7.5 tabulate>=0.7.5
future>=0.15.2 future>=0.15.2threadpool==1.3.2