mirror of
https://github.com/RicterZ/nhentai.git
synced 2025-07-01 16:09:28 +02:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
14b3c82248 | |||
4577e9df9a | |||
de157ccb7f | |||
126bbe8d49 | |||
8546b9e759 | |||
6ff9751c30 | |||
f316c3243b |
@ -5,9 +5,8 @@ language: python
|
||||
python:
|
||||
- 2.7
|
||||
- 2.6
|
||||
- 3.3
|
||||
- 3.4
|
||||
- 3.5.2
|
||||
- 3.6
|
||||
- 3.7
|
||||
|
||||
install:
|
||||
- python setup.py install
|
||||
@ -15,3 +14,4 @@ install:
|
||||
script:
|
||||
- NHENTAI=https://nhentai.net nhentai --search umaru
|
||||
- NHENTAI=https://nhentai.net nhentai --id=152503,146134 -t 10 --output=/tmp/
|
||||
- NHENTAI=https://nhentai.net nhentai -l nhentai_test:nhentai --output=/tmp/
|
||||
|
10
MANIFEST.in
10
MANIFEST.in
@ -1,5 +1,5 @@
|
||||
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
|
||||
|
138
README.md
138
README.md
@ -1,70 +1,70 @@
|
||||
nhentai
|
||||
=======
|
||||
_ _ _ _
|
||||
_ __ | | | | ___ _ __ | |_ __ _(_)
|
||||
| '_ \| |_| |/ _ \ '_ \| __/ _` | |
|
||||
| | | | _ | __/ | | | || (_| | |
|
||||
|_| |_|_| |_|\___|_| |_|\__\__,_|_|
|
||||
|
||||
あなたも変態。 いいね?
|
||||
[](https://travis-ci.org/RicterZ/nhentai)
|
||||
|
||||
🎉🎉 nhentai 现在支持 Windows 啦!
|
||||
|
||||
由于 [http://nhentai.net](http://nhentai.net) 下载下来的种子速度很慢,而且官方也提供在线观看本子的功能,所以可以利用本脚本下载本子。
|
||||
|
||||
### Installation
|
||||
|
||||
git clone https://github.com/RicterZ/nhentai
|
||||
cd nhentai
|
||||
python setup.py install
|
||||
|
||||
### Gentoo
|
||||
|
||||
layman -fa glicOne
|
||||
sudo emerge net-misc/nhentai
|
||||
|
||||
### Usage
|
||||
下载指定 id 列表的本子:
|
||||
```bash
|
||||
nhentai --id=123855,123866
|
||||
```
|
||||
|
||||
下载某关键词第一页的本子:
|
||||
```bash
|
||||
nhentai --search="tomori" --page=1 --download
|
||||
```
|
||||
|
||||
下载用户 favorites 内容:
|
||||
```bash
|
||||
nhentai --login "username:password" --download
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
`-t, --thread`:指定下载的线程数,最多为 10 线程。
|
||||
`--path`:指定下载文件的输出路径,默认为当前目录。
|
||||
`--timeout`:指定下载图片的超时时间,默认为 30 秒。
|
||||
`--proxy`:指定下载的代理,例如: http://127.0.0.1:8080/
|
||||
`--login`:nhentai 账号的“用户名:密码”组合
|
||||
`--nohtml`:nhentai Don't generate HTML
|
||||
`--cbz`:nhentai Generate Comic Book CBZ file
|
||||
|
||||
### nHentai Mirror
|
||||
如果想用自建镜像下载 nhentai 的本子,需要搭建 nhentai.net 和 i.nhentai.net 的反向代理。
|
||||
例如用 h.loli.club 来做反向代理的话,需要 h.loli.club 反代 nhentai.net,i.h.loli.club 反带 i.nhentai.net。
|
||||
然后利用环境变量来下载:
|
||||
|
||||
```bash
|
||||
NHENTAI=http://h.loli.club nhentai --id 123456
|
||||
```
|
||||
|
||||

|
||||

|
||||

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

|
||||

|
||||

|
||||
|
||||
### License
|
||||
MIT
|
||||
|
||||
### あなたも変態
|
||||

|
@ -1,3 +1,3 @@
|
||||
__version__ = '0.2.12'
|
||||
__version__ = '0.2.14'
|
||||
__author__ = 'RicterZ'
|
||||
__email__ = 'ricterzheng@gmail.com'
|
||||
|
@ -27,7 +27,7 @@ def login_parser(username, password):
|
||||
|
||||
s.get(constant.LOGIN_URL)
|
||||
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'})
|
||||
|
||||
if not csrf_token_elem:
|
||||
@ -44,14 +44,22 @@ def login_parser(username, password):
|
||||
logger.error('Login failed, please check your username and password')
|
||||
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'})
|
||||
if not count:
|
||||
logger.error('Cannot get count of your favorites, maybe login failed.')
|
||||
|
||||
count = int(count.text.strip('(').strip(')'))
|
||||
pages = count / 25
|
||||
pages += 1 if count % (25 * pages) else 0
|
||||
if count == 0:
|
||||
logger.warning('No favorites found')
|
||||
return []
|
||||
pages = int(count / 25)
|
||||
|
||||
if pages:
|
||||
pages += 1 if count % (25 * pages) else 0
|
||||
else:
|
||||
pages = 1
|
||||
|
||||
logger.info('Your have %d favorites in %d pages.' % (count, pages))
|
||||
|
||||
if os.getenv('DEBUG'):
|
||||
@ -68,7 +76,7 @@ def login_parser(username, password):
|
||||
for page in range(1, pages+1):
|
||||
try:
|
||||
logger.info('Getting doujinshi id of page %d' % page)
|
||||
resp = s.get(constant.FAV_URL + '?page=%d' % page).content
|
||||
resp = s.get(constant.FAV_URL + '?page=%d' % page).text
|
||||
ids = doujinshi_id.findall(resp)
|
||||
requests_ = threadpool.makeRequests(doujinshi_parser, ids, _callback)
|
||||
[thread_pool.putRequest(req) for req in requests_]
|
||||
|
@ -1,24 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{TITLE}</title>
|
||||
<style>
|
||||
{STYLES}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav id="list">
|
||||
{IMAGES}</nav>
|
||||
|
||||
<div id="image-container">
|
||||
<span id="page-num"></span>
|
||||
<div id="dest"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
{SCRIPTS}
|
||||
</script>
|
||||
</body>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{TITLE}</title>
|
||||
<style>
|
||||
{STYLES}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav id="list">
|
||||
{IMAGES}</nav>
|
||||
|
||||
<div id="image-container">
|
||||
<span id="page-num"></span>
|
||||
<div id="dest"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
{SCRIPTS}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1,62 +1,62 @@
|
||||
const pages = Array.from(document.querySelectorAll('img.image-item'));
|
||||
let currentPage = 0;
|
||||
|
||||
function changePage(pageNum) {
|
||||
const previous = pages[currentPage];
|
||||
const current = pages[pageNum];
|
||||
|
||||
if (current == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
previous.classList.remove('current');
|
||||
current.classList.add('current');
|
||||
|
||||
currentPage = pageNum;
|
||||
|
||||
const display = document.getElementById('dest');
|
||||
display.style.backgroundImage = `url("${current.src}")`;
|
||||
|
||||
document.getElementById('page-num')
|
||||
.innerText = [
|
||||
(pageNum + 1).toLocaleString(),
|
||||
pages.length.toLocaleString()
|
||||
].join('\u200a/\u200a');
|
||||
}
|
||||
|
||||
changePage(0);
|
||||
|
||||
document.getElementById('list').onclick = event => {
|
||||
if (pages.includes(event.target)) {
|
||||
changePage(pages.indexOf(event.target));
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById('image-container').onclick = event => {
|
||||
const width = document.getElementById('image-container').clientWidth;
|
||||
const clickPos = event.clientX / width;
|
||||
|
||||
if (clickPos < 0.5) {
|
||||
changePage(currentPage - 1);
|
||||
} else {
|
||||
changePage(currentPage + 1);
|
||||
}
|
||||
};
|
||||
|
||||
document.onkeypress = event => {
|
||||
switch (event.key.toLowerCase()) {
|
||||
// Previous Image
|
||||
case 'arrowleft':
|
||||
case 'a':
|
||||
changePage(currentPage - 1);
|
||||
break;
|
||||
|
||||
// Next Image
|
||||
case ' ':
|
||||
case 'enter':
|
||||
case 'arrowright':
|
||||
case 'd':
|
||||
changePage(currentPage + 1);
|
||||
break;
|
||||
}
|
||||
const pages = Array.from(document.querySelectorAll('img.image-item'));
|
||||
let currentPage = 0;
|
||||
|
||||
function changePage(pageNum) {
|
||||
const previous = pages[currentPage];
|
||||
const current = pages[pageNum];
|
||||
|
||||
if (current == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
previous.classList.remove('current');
|
||||
current.classList.add('current');
|
||||
|
||||
currentPage = pageNum;
|
||||
|
||||
const display = document.getElementById('dest');
|
||||
display.style.backgroundImage = `url("${current.src}")`;
|
||||
|
||||
document.getElementById('page-num')
|
||||
.innerText = [
|
||||
(pageNum + 1).toLocaleString(),
|
||||
pages.length.toLocaleString()
|
||||
].join('\u200a/\u200a');
|
||||
}
|
||||
|
||||
changePage(0);
|
||||
|
||||
document.getElementById('list').onclick = event => {
|
||||
if (pages.includes(event.target)) {
|
||||
changePage(pages.indexOf(event.target));
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById('image-container').onclick = event => {
|
||||
const width = document.getElementById('image-container').clientWidth;
|
||||
const clickPos = event.clientX / width;
|
||||
|
||||
if (clickPos < 0.5) {
|
||||
changePage(currentPage - 1);
|
||||
} else {
|
||||
changePage(currentPage + 1);
|
||||
}
|
||||
};
|
||||
|
||||
document.onkeypress = event => {
|
||||
switch (event.key.toLowerCase()) {
|
||||
// Previous Image
|
||||
case 'arrowleft':
|
||||
case 'a':
|
||||
changePage(currentPage - 1);
|
||||
break;
|
||||
|
||||
// Next Image
|
||||
case ' ':
|
||||
case 'enter':
|
||||
case 'arrowright':
|
||||
case 'd':
|
||||
changePage(currentPage + 1);
|
||||
break;
|
||||
}
|
||||
};
|
@ -1,69 +1,69 @@
|
||||
*, *::after, *::before {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
html, body {
|
||||
display: flex;
|
||||
background-color: #e8e6e6;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
#list {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
width: 260px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#list img {
|
||||
width: 200px;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
margin: 15px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#list img.current {
|
||||
background: #0003;
|
||||
}
|
||||
|
||||
#image-container {
|
||||
flex: auto;
|
||||
height: 100vh;
|
||||
background: #222;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#image-container #dest {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
#image-container #page-num {
|
||||
position: absolute;
|
||||
font-size: 18pt;
|
||||
left: 10px;
|
||||
bottom: 5px;
|
||||
font-weight: bold;
|
||||
opacity: 0.75;
|
||||
text-shadow: /* Duplicate the same shadow to make it very strong */
|
||||
0 0 2px #222,
|
||||
0 0 2px #222,
|
||||
0 0 2px #222;
|
||||
*, *::after, *::before {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
html, body {
|
||||
display: flex;
|
||||
background-color: #e8e6e6;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
#list {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
width: 260px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#list img {
|
||||
width: 200px;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
margin: 15px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#list img.current {
|
||||
background: #0003;
|
||||
}
|
||||
|
||||
#image-container {
|
||||
flex: auto;
|
||||
height: 100vh;
|
||||
background: #222;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#image-container #dest {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
#image-container #page-num {
|
||||
position: absolute;
|
||||
font-size: 18pt;
|
||||
left: 10px;
|
||||
bottom: 5px;
|
||||
font-weight: bold;
|
||||
opacity: 0.75;
|
||||
text-shadow: /* Duplicate the same shadow to make it very strong */
|
||||
0 0 2px #222,
|
||||
0 0 2px #222,
|
||||
0 0 2px #222;
|
||||
}
|
@ -2,4 +2,4 @@ requests>=2.5.0
|
||||
BeautifulSoup4>=4.0.0
|
||||
threadpool>=1.2.7
|
||||
tabulate>=0.7.5
|
||||
future>=0.15.2
|
||||
future>=0.15.2threadpool==1.3.2
|
||||
|
Reference in New Issue
Block a user