1
0
mirror of https://github.com/mikf/gallery-dl.git synced 2024-11-23 19:22:32 +01:00
gallery-dl/gallery_dl/extractor/exhentai.py

225 lines
8.1 KiB
Python
Raw Normal View History

2015-10-31 16:50:20 +01:00
# -*- coding: utf-8 -*-
2017-03-17 09:42:59 +01:00
# Copyright 2014-2017 Mike Fährmann
2015-10-31 16:50:20 +01:00
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
2016-08-30 09:17:40 +02:00
"""Extract images from galleries at https://exhentai.org/"""
2015-10-31 16:50:20 +01:00
from .common import Extractor, Message
from .. import text, util, exception
from ..cache import cache
2014-10-12 21:56:44 +02:00
import time
import random
2016-10-11 13:27:19 +02:00
import requests
2014-10-12 21:56:44 +02:00
2017-02-01 00:53:19 +01:00
class ExhentaiGalleryExtractor(Extractor):
"""Extractor for image-galleries from exhentai.org"""
2015-11-21 04:26:30 +01:00
category = "exhentai"
subcategory = "gallery"
2015-11-21 04:26:30 +01:00
directory_fmt = ["{category}", "{gallery-id}"]
2016-09-20 19:01:16 +02:00
filename_fmt = "{gallery-id}_{num:>04}_{image-token}_{name}.{extension}"
pattern = [r"(?:https?://)?(g\.e-|e-|ex)hentai\.org/g/(\d+)/([\da-f]{10})"]
2016-12-31 00:51:06 +01:00
test = [
("https://exhentai.org/g/960460/4f0e369d82/", {
"keyword": "623f8c86c9fe38e964682dd4309b96922655b900",
"content": "493d759de534355c9f55f8e365565b62411de146",
}),
("https://exhentai.org/g/960461/4f0e369d82/", {
"exception": exception.NotFoundError,
}),
("http://exhentai.org/g/962698/7f02358e00/", {
"exception": exception.AuthorizationError,
}),
]
root = "https://exhentai.org"
2014-10-12 21:56:44 +02:00
2015-10-31 16:50:20 +01:00
def __init__(self, match):
Extractor.__init__(self)
2016-09-20 19:01:16 +02:00
self.key = {}
self.count = 0
self.version, self.gid, self.token = match.groups()
self.original = self.config("original", True)
self.wait_min = self.config("wait-min", 3)
self.wait_max = self.config("wait-max", 6)
2015-11-19 17:04:54 +01:00
if self.wait_max < self.wait_min:
self.wait_max = self.wait_min
2014-10-12 21:56:44 +02:00
2015-10-31 16:50:20 +01:00
def items(self):
2016-09-20 19:01:16 +02:00
self.login()
2015-10-31 16:50:20 +01:00
yield Message.Version, 1
2016-09-20 19:01:16 +02:00
yield Message.Headers, self.setup_headers()
yield Message.Cookies, self.session.cookies
url = "{}/g/{}/{}/".format(self.root, self.gid, self.token)
response = self.session.get(url)
page = response.text
if response.status_code == 404 and "Gallery Not Available" in page:
raise exception.AuthorizationError()
if page.startswith(("Key missing", "Gallery not found")):
2016-08-30 09:17:40 +02:00
raise exception.NotFoundError("gallery")
2016-09-20 19:01:16 +02:00
data = self.get_job_metadata(page)
self.count = int(data["count"])
2015-10-31 16:50:20 +01:00
yield Message.Directory, data
2014-10-12 21:56:44 +02:00
2016-09-20 19:01:16 +02:00
for url, image in self.get_images(page):
data.update(image)
2016-09-19 16:13:26 +02:00
if "/fullimg.php" in url:
data["extension"] = ""
2015-11-19 17:04:54 +01:00
self.wait((1, 2))
2016-09-20 19:01:16 +02:00
yield Message.Url, url, data
def setup_headers(self):
"""Initialize headers"""
self.session.headers.update({
"User-Agent": "Mozilla/5.0",
2017-02-01 00:53:19 +01:00
"Accept": "text/html,application/xhtml+xml,"
"application/xml;q=0.9,*/*;q=0.8",
2016-09-20 19:01:16 +02:00
"Accept-Language": "en-US,en;q=0.5",
"Referer": self.root + "/",
2016-09-20 19:01:16 +02:00
})
headers = self.session.headers.copy()
headers["Accept"] = "image/png,image/*;q=0.8,*/*;q=0.5"
return headers
2014-10-12 21:56:44 +02:00
2015-10-31 16:50:20 +01:00
def get_job_metadata(self, page):
2015-11-03 00:10:30 +01:00
"""Collect metadata for extractor-job"""
data = {
"gallery-id" : self.gid,
2015-10-31 16:50:20 +01:00
"gallery-token": self.token,
2015-11-03 00:10:30 +01:00
}
2016-09-20 19:01:16 +02:00
text.extract_all(page, (
("title" , '<h1 id="gn">', '</h1>'),
("title_jp" , '<h1 id="gj">', '</h1>'),
("date" , '>Posted:</td><td class="gdt2">', '</td>'),
("language" , '>Language:</td><td class="gdt2">', ' '),
("size" , '>File Size:</td><td class="gdt2">', ' '),
("size-units", '', '<'),
("count" , '>Length:</td><td class="gdt2">', ' '),
2015-11-03 00:10:30 +01:00
), values=data)
2017-03-28 13:12:44 +02:00
data["lang"] = util.language_to_code(data["language"])
2016-08-31 10:20:46 +02:00
data["title"] = text.unescape(data["title"])
data["title_jp"] = text.unescape(data["title_jp"])
2016-09-20 19:01:16 +02:00
return data
2014-10-12 21:56:44 +02:00
2016-09-20 19:01:16 +02:00
def get_images(self, page):
2015-11-03 00:10:30 +01:00
"""Collect url and metadata for all images in this gallery"""
2017-02-01 00:53:19 +01:00
part = text.extract(page, 'hentai.org/s/', '"')[0]
yield self.image_from_page(self.root + "/s/" + part)
2016-09-20 19:01:16 +02:00
yield from self.images_from_api()
def image_from_page(self, url):
"""Get image url and data from webpage"""
2015-11-19 17:04:54 +01:00
self.wait()
2015-10-31 16:50:20 +01:00
page = self.request(url).text
2016-09-20 19:01:16 +02:00
data = text.extract_all(page, (
(None , '<div id="i3"><a onclick="return load_image(', ''),
("nextkey" , "'", "'"),
("url" , '<img id="img" src="', '"'),
("origurl" , 'hentai.org/fullimg.php', '"'),
2016-09-20 19:01:16 +02:00
("startkey", 'var startkey="', '";'),
("showkey" , 'var showkey="', '";'),
))[0]
self.key["start"] = data["startkey"]
2017-02-01 00:53:19 +01:00
self.key["show"] = data["showkey"]
self.key["next"] = data["nextkey"]
if self.original and data["origurl"]:
part = text.unescape(data["origurl"])
url = self.root + "/fullimg.php" + part
2017-02-01 00:53:19 +01:00
else:
url = data["url"]
2016-09-20 19:01:16 +02:00
return url, text.nameext_from_url(data["url"], {
"num": 1,
"image-token": data["startkey"],
})
2016-09-20 19:01:16 +02:00
def images_from_api(self):
"""Get image url and data from api calls"""
api_url = self.root + "/api.php"
2017-02-01 00:53:19 +01:00
nextkey = self.key["next"]
2014-10-12 21:56:44 +02:00
request = {
"method" : "showpage",
2016-09-19 16:13:26 +02:00
"gid" : int(self.gid),
2016-09-20 19:01:16 +02:00
"imgkey" : nextkey,
"showkey": self.key["show"],
2014-10-12 21:56:44 +02:00
}
2017-02-01 00:53:19 +01:00
for request["page"] in range(2, self.count + 1):
2016-10-11 13:27:19 +02:00
while True:
try:
self.wait()
page = self.session.post(api_url, json=request).json()
2016-10-11 13:27:19 +02:00
break
except requests.exceptions.ConnectionError:
pass
2016-09-20 19:01:16 +02:00
imgkey = nextkey
nextkey, pos = text.extract(page["i3"], "'", "'")
2017-02-01 00:53:19 +01:00
imgurl , pos = text.extract(page["i3"], 'id="img" src="', '"', pos)
2016-09-20 19:01:16 +02:00
origurl, pos = text.extract(page["i7"], '<a href="', '"')
2017-02-01 00:53:19 +01:00
if self.original and origurl:
url = text.unescape(origurl)
else:
url = imgurl
2016-09-20 19:01:16 +02:00
yield url, text.nameext_from_url(imgurl, {
"num": request["page"],
"image-token": imgkey
})
request["imgkey"] = nextkey
2015-11-19 17:04:54 +01:00
def wait(self, waittime=None):
"""Wait for a randomly chosen amount of seconds"""
if not waittime:
waittime = random.uniform(self.wait_min, self.wait_max)
else:
waittime = random.uniform(*waittime)
time.sleep(waittime)
def login(self):
"""Login and set necessary cookies"""
username, password = self.auth_info()
if not username:
self.log.info("no username given; using e-hentai.org")
self.root = "https://e-hentai.org"
self.original = False
return
cookies = self._login_impl(username, password)
for key, value in cookies.items():
2017-02-01 00:53:19 +01:00
self.session.cookies.set(
key, value, domain=".exhentai.org", path="/")
2017-03-17 09:42:59 +01:00
@cache(maxage=90*24*60*60, keyarg=1)
def _login_impl(self, username, password):
"""Actual login implementation"""
2017-03-17 09:42:59 +01:00
self.log.info("Logging in as %s", username)
cnames = ["ipb_member_id", "ipb_pass_hash"]
try:
cookies = self.config("cookies")
if isinstance(cookies, dict) and all(c in cookies for c in cnames):
return cookies
except TypeError:
pass
url = "https://forums.e-hentai.org/index.php?act=Login&CODE=01"
params = {
"CookieDate": "1",
"b": "d",
"bt": "1-1",
"UserName": username,
"PassWord": password,
"ipb_login_submit": "Login!",
}
referer = "https://e-hentai.org/bounce_login.php?b=d&bt=1-1"
2017-02-01 00:53:19 +01:00
self.session.headers["Referer"] = referer
response = self.session.post(url, data=params)
if "You are now logged in as:" not in response.text:
raise exception.AuthenticationError()
return {c: response.cookies[c] for c in cnames}