Support non-H@H archive download

This commit is contained in:
FooIbar authored and tarsin committed 2023-01-03 08:53:25 +08:00
1 parent 3b5e56b049
commit 23acdb6492
6 files changed
+134 -31

No files matched your search

@@ -167,7 +167,7 @@ public class EhClient {
case METHOD_ARCHIVE_LIST ->
EhEngine.getArchiveList(this, mOkHttpClient, (String) params[0], (Long) params[1], (String) params[2]);
case METHOD_DOWNLOAD_ARCHIVE ->
EhEngine.downloadArchive(this, mOkHttpClient, (Long) params[0], (String) params[1], (String) params[2], (String) params[3]);
EhEngine.downloadArchive(this, mOkHttpClient, (Long) params[0], (String) params[1], (String) params[2], (String) params[3], (Boolean) params[4]);
case METHOD_VOTE_TAG ->
EhEngine.voteTag(this, mOkHttpClient, (Long) params[0], (String) params[1], (Long) params[2], (String) params[3], (String) params[4], (Integer) params[5]);
case METHOD_GET_UCONFIG -> EhEngine.getUConfig(this, mOkHttpClient);
@@ -33,7 +33,6 @@ import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.client.data.PreviewSet;
import com.hippo.ehviewer.client.exception.CancelledException;
import com.hippo.ehviewer.client.exception.EhException;
import com.hippo.ehviewer.client.exception.NoHAtHClientException;
import com.hippo.ehviewer.client.exception.ParseException;
import com.hippo.ehviewer.client.parser.ArchiveParser;
import com.hippo.ehviewer.client.parser.EventPaneParser;
@@ -65,8 +64,6 @@ import org.jsoup.select.Elements;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import okhttp3.Call;
import okhttp3.FormBody;
@@ -88,8 +85,6 @@ public class EhEngine {
private static final String KOKOMADE_URL = "https://exhentai.org/img/kokomade.jpg";
private static final MediaType MEDIA_TYPE_JPEG = MediaType.parse("image/jpeg");
private static final Pattern PATTERN_NEED_HATH_CLIENT = Pattern.compile("(You must have a H@H client assigned to your account to use this feature\\.)");
public static EhFilter sEhFilter;
public static void initialize() {
@@ -714,8 +709,8 @@ public class EhEngine {
return result;
}
public static Void downloadArchive(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
long gid, String token, String or, String res) throws Throwable {
public static String downloadArchive(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
long gid, String token, String or, String res, boolean isHAtH) throws Throwable {
if (or == null || or.length() == 0) {
throw new EhException("Invalid form param or: " + or);
}
@@ -723,7 +718,16 @@ public class EhEngine {
throw new EhException("Invalid res: " + res);
}
FormBody.Builder builder = new FormBody.Builder();
builder.add("hathdl_xres", res);
if (isHAtH) {
builder.add("hathdl_xres", res);
} else {
builder.add("dltype", res);
if (res.equals("org")) {
builder.add("dlcheck", "Download Original Archive");
} else {
builder.add("dlcheck", "Download Resample Archive");
}
}
String url = EhUrl.getDownloadArchive(gid, token, or);
String referer = EhUrl.getGalleryDetailUrl(gid, token);
String origin = EhUrl.getOrigin();
@@ -752,11 +756,27 @@ public class EhEngine {
throw e;
}
Matcher m = PATTERN_NEED_HATH_CLIENT.matcher(body);
if (m.find()) {
throw new NoHAtHClientException("No H@H client");
var result = ArchiveParser.parseArchiveUrl(body);
if (!isHAtH) {
if (result == null) {
Thread.sleep(1000);
try (var response = call.clone().execute()) {
code = response.code();
headers = response.headers();
body = response.body().string();
throwException(call, code, headers, body, null);
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
throwException(call, code, headers, body, e);
throw e;
}
result = ArchiveParser.parseArchiveUrl(body);
if (result == null) {
throw new EhException("Archive unavailable");
}
}
return result;
}
return null;
}
@@ -18,51 +18,101 @@ package com.hippo.ehviewer.client.parser;
import androidx.annotation.NonNull;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.client.exception.NoHAtHClientException;
import org.jsoup.Jsoup;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.IntFunction;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ArchiveParser {
private static final Pattern PATTERN_FORM = Pattern.compile("<form id=\"hathdl_form\" action=\"[^\"]*?or=([^=\"]*?)\" method=\"post\">");
private static final Pattern PATTERN_ARCHIVE = Pattern.compile("<p><a href=\"[^\"]*\" onclick=\"return do_hathdl\\('([0-9]+|org)'\\)\">([^<]+)</a></p>\\s*<p>([\\w. ]+)</p>\\s*<p>([\\w. ]+)</p>");
private static final Pattern PATTERN_ARCHIVE_URL = Pattern.compile("<strong>(.*)</strong>.*<a href=\"([^\"]*)\">Click Here To Start Downloading</a>");
private static final Pattern PATTERN_HATH_FORM = Pattern.compile("<form id=\"hathdl_form\" action=\"[^\"]*?or=([^=\"]*?)\" method=\"post\">");
private static final Pattern PATTERN_HATH_ARCHIVE = Pattern.compile("<p><a href=\"[^\"]*\" onclick=\"return do_hathdl\\('([0-9]+|org)'\\)\">([^<]+)</a></p>\\s*<p>([\\w. ]+)</p>\\s*<p>([\\w. ]+)</p>");
private static final Pattern PATTERN_NEED_HATH_CLIENT = Pattern.compile("You must have a H@H client assigned to your account to use this feature\\.");
public static Result parse(String body) {
Matcher m = PATTERN_FORM.matcher(body);
Matcher m = PATTERN_HATH_FORM.matcher(body);
if (!m.find()) {
return new Result(null, null);
}
String paramOr = m.group(1);
var archiveList = new ArrayList<Archive>();
m = PATTERN_ARCHIVE.matcher(body);
var d = Jsoup.parse(body);
var es = d.select("#db>div>div");
for (var e : es) {
try {
var res = e.selectFirst("form>input").attr("value");
var name = e.selectFirst("form>div>input").attr("value");
var size = e.selectFirst("p>strong").text();
var cost = e.selectFirst("div>strong").text();
var item = new Archive(res, name, size, cost, false);
archiveList.add(item);
} catch (NullPointerException ex) {
ex.printStackTrace();
}
}
m = PATTERN_HATH_ARCHIVE.matcher(body);
while (m.find()) {
String res = ParserUtils.trim(m.group(1));
String name = ParserUtils.trim(m.group(2));
String size = ParserUtils.trim(m.group(3));
String cost = ParserUtils.trim(m.group(4));
var item = new Archive(res, name, size, cost);
var item = new Archive(res, name, size, cost, true);
archiveList.add(item);
}
return new Result(paramOr, archiveList);
}
public static String parseArchiveUrl(String body) throws NoHAtHClientException {
Matcher m = PATTERN_NEED_HATH_CLIENT.matcher(body);
if (m.find()) {
throw new NoHAtHClientException("No H@H client");
}
var d = Jsoup.parse(body);
var a = d.selectFirst("#continue>a[href]");
if (a != null) {
return a.attr("href") + "?start=1";
}
// TODO: Check more errors
return null;
}
public static final class Archive {
private final String res;
private final String name;
private final String size;
private final String cost;
private final boolean isHAtH;
public Archive(String res, String name, String size, String cost) {
public Archive(String res, String name, String size, String cost, boolean isHAtH) {
this.res = res;
this.name = name;
this.size = size;
this.cost = cost;
this.isHAtH = isHAtH;
}
public String format() {
return String.format("%s [%s, %s]", name, size, cost);
public String format(IntFunction<String> getString) {
if (isHAtH) {
var costStr = cost.equals("Free") ? getString.apply(R.string.archive_free) : cost;
return String.format("[H@H] %s [%s] [%s]", name, size, costStr);
} else {
var nameStr = getString.apply(res.equals("org") ? R.string.archive_original : R.string.archive_resample);
var costStr = cost.equals("Free!") ? getString.apply(R.string.archive_free) : cost;
return String.format("%s [%s] [%s]", nameStr, size, costStr);
}
}
public String res() {
@@ -81,6 +131,10 @@ public class ArchiveParser {
return cost;
}
public boolean isHAtH() {
return isHAtH;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
@@ -89,12 +143,13 @@ public class ArchiveParser {
return Objects.equals(this.res, that.res) &&
Objects.equals(this.name, that.name) &&
Objects.equals(this.size, that.size) &&
Objects.equals(this.cost, that.cost);
Objects.equals(this.cost, that.cost) &&
this.isHAtH == that.isHAtH;
}
@Override
public int hashCode() {
return Objects.hash(res, name, size, cost);
return Objects.hash(res, name, size, cost, isHAtH);
}
@NonNull
@@ -104,7 +159,8 @@ public class ArchiveParser {
"res=" + res + ", " +
"name=" + name + ", " +
"size=" + size + ", " +
"cost=" + cost + ']';
"cost=" + cost + ", " +
"isHAtH=" + isHAtH + ']';
}
}
@@ -1177,7 +1177,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
}
ArchiveListDialogHelper helper = new ArchiveListDialogHelper();
Dialog dialog = new BaseDialogBuilder(context)
.setTitle(R.string.dialog_archive_title)
.setTitle(R.string.settings_download)
.setView(R.layout.dialog_archive_list)
.setOnDismissListener(helper)
.show();
@@ -1713,14 +1713,34 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
}
}
private static class DownloadArchiveListener extends EhCallback<GalleryDetailScene, Void> {
private static class DownloadArchiveListener extends EhCallback<GalleryDetailScene, String> {
public DownloadArchiveListener(Context context, int stageId, String sceneTag) {
private final GalleryInfo mGalleryInfo;
public DownloadArchiveListener(Context context, int stageId, String sceneTag, GalleryInfo galleryInfo) {
super(context, stageId, sceneTag);
mGalleryInfo = galleryInfo;
}
@Override
public void onSuccess(Void result) {
public void onSuccess(String result) {
if (result != null) {
// TODO: Don't use buggy system download service
DownloadManager.Request r = new DownloadManager.Request(Uri.parse(result));
var name = mGalleryInfo.gid + "-" + EhUtils.getSuitableTitle(mGalleryInfo) + ".zip";
r.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS,
FileUtils.sanitizeFilename(name));
r.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
DownloadManager dm = (DownloadManager) getApplication().getSystemService(Context.DOWNLOAD_SERVICE);
if (dm != null) {
try {
dm.enqueue(r);
} catch (Throwable e) {
e.printStackTrace();
ExceptionUtils.throwIfFatal(e);
}
}
}
showTip(R.string.download_archive_started, LENGTH_SHORT);
}
@@ -1790,7 +1810,7 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
mListView.setVisibility(View.GONE);
mErrorText.setText(R.string.no_archives);
} else {
var nameArray = data.stream().map(ArchiveParser.Archive::format).toArray(String[]::new);
var nameArray = data.stream().map(archive -> archive.format(getResources()::getString)).toArray(String[]::new);
mProgressView.setVisibility(View.GONE);
mErrorText.setVisibility(View.GONE);
mListView.setVisibility(View.VISIBLE);
@@ -1804,10 +1824,11 @@ public class GalleryDetailScene extends CollapsingToolbarScene implements View.O
MainActivity activity = getMainActivity();
if (null != context && null != activity && null != mArchiveList && position < mArchiveList.size()) {
String res = mArchiveList.get(position).res();
boolean isHAtH = mArchiveList.get(position).isHAtH();
EhRequest request = new EhRequest();
request.setMethod(EhClient.METHOD_DOWNLOAD_ARCHIVE);
request.setArgs(mGalleryDetail.gid, mGalleryDetail.token, mArchiveFormParamOr, res);
request.setCallback(new DownloadArchiveListener(context, activity.getStageId(), getTag()));
request.setArgs(mGalleryDetail.gid, mGalleryDetail.token, mArchiveFormParamOr, res, isHAtH);
request.setCallback(new DownloadArchiveListener(context, activity.getStageId(), getTag(), mGalleryDetail));
EhApplication.getEhClient().execute(request);
}
@@ -531,6 +531,9 @@
<string name="download_archive_started">开始下载压缩包</string>
<string name="download_archive_failure">无法下载压缩包</string>
<string name="archive">压缩包</string>
<string name="archive_free">免费</string>
<string name="archive_original">原始档案</string>
<string name="archive_resample">重采样档案</string>
<string name="download_archive_failure_no_hath">下载压缩包需要 H@H 客户端</string>
<string name="settings_privacy">隐私</string>
<string name="settings_privacy_secure">不允许屏幕抓取</string>
+3
View File
@@ -598,6 +598,9 @@
<string name="download_archive_started">Archive download started</string>
<string name="download_archive_failure">Failed to download archive</string>
<string name="archive">Archive</string>
<string name="archive_free">Free</string>
<string name="archive_original">Original archive</string>
<string name="archive_resample">Resample archive</string>
<string name="download_archive_failure_no_hath">Need H@H client for archive download</string>
<string name="settings_privacy">Privacy</string>
<string name="settings_privacy_secure">Prevent screenshots</string>