|
|
@@ -4,144 +4,71 @@ import config.downloaderconfig; |
|
|
|
import sites.basesite; |
|
|
|
|
|
|
|
/++ |
|
|
|
+ This class handles downloads for the site `nhentai` |
|
|
|
+ This class handles downloads for the site `nhentai.net` |
|
|
|
+/ |
|
|
|
class NHentai : BaseSite |
|
|
|
{ |
|
|
|
private |
|
|
|
import std.conv : to; |
|
|
|
import std.net.curl : get; |
|
|
|
import std.json : JSONValue, parseJSON; |
|
|
|
import std.array : split; |
|
|
|
import std.conv : to; |
|
|
|
import std.regex : regex, match; |
|
|
|
import std.net.curl : get; |
|
|
|
import core.stdc.stdlib : exit, EXIT_FAILURE; |
|
|
|
|
|
|
|
/++ |
|
|
|
+ This struct holds all the needed infos about the nhentai doujin |
|
|
|
+ This is the base url for all images |
|
|
|
+/ |
|
|
|
struct NHentai_Doujin_Info |
|
|
|
{ |
|
|
|
/++ |
|
|
|
+ This is the number of the manga |
|
|
|
+/ |
|
|
|
string number; |
|
|
|
|
|
|
|
/++ |
|
|
|
+ This is the title of the the manga |
|
|
|
+/ |
|
|
|
string title; |
|
|
|
|
|
|
|
/++ |
|
|
|
+ This array holds all the urls of the images |
|
|
|
+/ |
|
|
|
string[] imageUrls; |
|
|
|
} |
|
|
|
|
|
|
|
/++ |
|
|
|
+ This is the url of the nhentai api |
|
|
|
+ calls are made by the number of the manga |
|
|
|
+ for example "https://apis.nhent.ai/g/1" |
|
|
|
+ |
|
|
|
+ The returned json string contains all the info |
|
|
|
+ should be read into `NHenta_Doujin_Info` |
|
|
|
+/ |
|
|
|
immutable string api_url = "https://apis.nhent.ai/g/"; |
|
|
|
immutable string imageUrl = "https://i.nhentai.net/galleries/"; |
|
|
|
|
|
|
|
/++ |
|
|
|
+ This variable holds the class internal |
|
|
|
+ number of the manga |
|
|
|
+ This function gets the name of the the manga by the url |
|
|
|
+/ |
|
|
|
string _number; |
|
|
|
|
|
|
|
/++ |
|
|
|
+ This struct contains all the needed infos |
|
|
|
+ to download the managa |
|
|
|
+/ |
|
|
|
NHentai_Doujin_Info _nhentai_doujin_info; |
|
|
|
|
|
|
|
/++ |
|
|
|
+ This function extracts the number of the manga |
|
|
|
+ from the supplied url |
|
|
|
+/ |
|
|
|
string extractNumFromUrl(string url) |
|
|
|
{ |
|
|
|
string[] tmpString = url.split("/"); |
|
|
|
// FIXME: length could be unsigned so substract bad! |
|
|
|
return tmpString[tmpString.length-2]; |
|
|
|
} |
|
|
|
|
|
|
|
/++ |
|
|
|
+ This function gets the info of of the doujin using the api |
|
|
|
+ it returns a struct with all the important info |
|
|
|
+/ |
|
|
|
NHentai_Doujin_Info getDoujinInfo(string mangaNum) |
|
|
|
override string getNameFromUrl(string url) |
|
|
|
{ |
|
|
|
NHentai_Doujin_Info _info; |
|
|
|
|
|
|
|
// Craft the url |
|
|
|
string requestUrl = api_url ~ mangaNum; |
|
|
|
|
|
|
|
// Get the json data for the manga |
|
|
|
string jsonData = to!string(get(requestUrl)); |
|
|
|
// Get the site content |
|
|
|
string siteContent = to!string(get(url)); |
|
|
|
|
|
|
|
// Extract the image urls from the json string |
|
|
|
_info.imageUrls = getUrlsFromJson(jsonData); |
|
|
|
// Find the name of the manga |
|
|
|
auto nameRegex = `<h1>(.*)</h1>`.regex; |
|
|
|
auto nameMatch = match(siteContent, nameRegex); |
|
|
|
|
|
|
|
// Parse the data |
|
|
|
auto parseData = parseJSON(jsonData); |
|
|
|
|
|
|
|
// Get the title |
|
|
|
_info.title = parseData["title"].str(); |
|
|
|
|
|
|
|
return _info; |
|
|
|
// Return only the name not the hmtl tags |
|
|
|
return nameMatch.captures[1]; |
|
|
|
} |
|
|
|
|
|
|
|
/++ |
|
|
|
+ If the class internal info struct is filled |
|
|
|
+ but the number is different `getDoujinInfo` gets |
|
|
|
+ called otherwise nothing happens |
|
|
|
+/ |
|
|
|
void fetchInfoForManaga(string number) |
|
|
|
override string[] getImageUrlsFromBase(string url) |
|
|
|
{ |
|
|
|
// If the doujin info wasnt fetched fetch it now |
|
|
|
if(_nhentai_doujin_info.number != number) |
|
|
|
// Check if the url is a nhentai url |
|
|
|
if(indexOf(url, "/g/") == -1) |
|
|
|
{ |
|
|
|
writeln("\nGetting info...."); |
|
|
|
// Fill the info |
|
|
|
_nhentai_doujin_info = getDoujinInfo(number); |
|
|
|
_nhentai_doujin_info.number = number; |
|
|
|
writefln(`[!] The given url doesn't contain "/g/" it was ignored!`); |
|
|
|
// FIXME: no! :< |
|
|
|
exit(EXIT_FAILURE); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
protected: |
|
|
|
override string getNameFromUrl(string url) |
|
|
|
{ |
|
|
|
// Extract the manga number |
|
|
|
_number = extractNumFromUrl(url); |
|
|
|
|
|
|
|
// Fetch manga infos |
|
|
|
fetchInfoForManaga(_number); |
|
|
|
// Regex patterns for finding ulrs and stuff |
|
|
|
auto contentIDRegex = "https://t.nhentai.net/galleries/([0-9].*)/cover.jpg"; |
|
|
|
auto pageCountRegex = "<div>([0-9].*) pages</div>"; |
|
|
|
|
|
|
|
// Return the name of the managa |
|
|
|
return _nhentai_doujin_info.title; |
|
|
|
} |
|
|
|
// Download the hmtl |
|
|
|
auto coverHtml = to!string(get(url)); |
|
|
|
|
|
|
|
override string[] getImageUrlsFromBase(string url) |
|
|
|
{ |
|
|
|
// Fetch info if it wanst already fetched |
|
|
|
fetchInfoForManaga(_number); |
|
|
|
// Find the content id |
|
|
|
auto contentIDMatch = match(coverHtml, contentIDRegex).captures[1]; |
|
|
|
|
|
|
|
return _nhentai_doujin_info.imageUrls; |
|
|
|
} |
|
|
|
writeln(contentIDMatch); |
|
|
|
|
|
|
|
override string[] getUrlsFromJson(string json) |
|
|
|
{ |
|
|
|
// Extract url from json |
|
|
|
string[] urls; |
|
|
|
// Find the number of pages |
|
|
|
auto pageNumberMatch = match(coverHtml, pageCountRegex).captures[1]; |
|
|
|
|
|
|
|
JSONValue parsedJson = parseJSON(json); |
|
|
|
// Convert the page number to an integer |
|
|
|
immutable int pageNumber = to!int(pageNumberMatch); |
|
|
|
|
|
|
|
// Extract the urls for the images |
|
|
|
foreach(JSONValue val; parsedJson["pages"].array()) |
|
|
|
urls ~= val.str().replace("i.bakaa.me", "i.nhentai.net"); |
|
|
|
// Generate a list of all the images |
|
|
|
string[] urls; |
|
|
|
for(int i = 1; i < pageNumber; i++) |
|
|
|
{ |
|
|
|
// Craft the url with all parameters |
|
|
|
urls ~= imageUrl ~ contentIDMatch ~ "/" ~ to!string(i) ~ ".jpg"; |
|
|
|
} |
|
|
|
|
|
|
|
return urls; |
|
|
|
} |
|
|
|