Tweak readahead logic
This commit is contained in:
1 parent
1d8b5f81a1
commit
933a95023c
3 files changed
+217
-204
No files matched your search
@@ -124,7 +124,7 @@ public class EhPageLoader extends PageLoader2 implements SpiderQueen.OnSpiderLis
|
||||
@Override
|
||||
protected void onRequest(int index) {
|
||||
if (mSpiderQueen != null) {
|
||||
Object object = mSpiderQueen.request(index, true);
|
||||
Object object = mSpiderQueen.request(index);
|
||||
if (object instanceof Float) {
|
||||
notifyPagePercent(index, (Float) object);
|
||||
} else if (object instanceof String) {
|
||||
@@ -185,7 +185,7 @@ public class EhPageLoader extends PageLoader2 implements SpiderQueen.OnSpiderLis
|
||||
@Override
|
||||
public void onPageSuccess(int index, int finished, int downloaded, int total) {
|
||||
if (mSpiderQueen != null) {
|
||||
mSpiderQueen.request(index, false);
|
||||
mSpiderQueen.request(index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ import androidx.collection.LongSparseArray;
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.GetText;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.Settings;
|
||||
import com.hippo.ehviewer.client.EhRequestBuilder;
|
||||
import com.hippo.ehviewer.client.EhUrl;
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo;
|
||||
@@ -40,7 +39,6 @@ import com.hippo.ehviewer.client.parser.GalleryPageUrlParser;
|
||||
import com.hippo.image.Image;
|
||||
import com.hippo.unifile.UniFile;
|
||||
import com.hippo.util.ExceptionUtils;
|
||||
import com.hippo.yorozuya.MathUtils;
|
||||
import com.hippo.yorozuya.OSUtils;
|
||||
import com.hippo.yorozuya.thread.PriorityThread;
|
||||
|
||||
@@ -100,11 +98,9 @@ public final class SpiderQueen implements Runnable {
|
||||
private final Object mPageStateLock = new Object();
|
||||
private final AtomicInteger mDownloadedPages = new AtomicInteger(0);
|
||||
private final AtomicInteger mFinishedPages = new AtomicInteger(0);
|
||||
private final Object showKeyLock = new Object();
|
||||
// Store page error
|
||||
private final ConcurrentHashMap<Integer, String> mPageErrorMap = new ConcurrentHashMap<>();
|
||||
private final List<OnSpiderListener> mSpiderListeners = new ArrayList<>();
|
||||
private final int mPreloadNumber;
|
||||
public volatile int[] mPageStateArray;
|
||||
private int mReadReference = 0;
|
||||
private int mDownloadReference = 0;
|
||||
@@ -121,8 +117,6 @@ public final class SpiderQueen implements Runnable {
|
||||
mGalleryInfo = galleryInfo;
|
||||
mSpiderDen = new SpiderDen(mGalleryInfo);
|
||||
|
||||
mPreloadNumber = MathUtils.clamp(Settings.getPreloadImage(), 0, 100);
|
||||
|
||||
for (int i = 0; i < DECODE_THREAD_NUM; i++) {
|
||||
mDecodeIndexArray[i] = -1;
|
||||
}
|
||||
@@ -388,11 +382,11 @@ public final class SpiderQueen implements Runnable {
|
||||
}
|
||||
|
||||
public Object forceRequest(int index) {
|
||||
return request(index, true, true, false);
|
||||
return request(index, true, true);
|
||||
}
|
||||
|
||||
public Object request(int index, boolean addNeighbor) {
|
||||
return request(index, true, false, addNeighbor);
|
||||
public Object request(int index) {
|
||||
return request(index, true, false);
|
||||
}
|
||||
|
||||
private int getPageState(int index) {
|
||||
@@ -410,7 +404,7 @@ public final class SpiderQueen implements Runnable {
|
||||
return;
|
||||
}
|
||||
|
||||
mWorkerScope.removeRequested(index);
|
||||
mWorkerScope.cancel(index);
|
||||
synchronized (mDecodeRequestQueue) {
|
||||
mDecodeRequestQueue.remove(index);
|
||||
}
|
||||
@@ -421,9 +415,7 @@ public final class SpiderQueen implements Runnable {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i : pages) {
|
||||
mWorkerScope.launchIndexed(i);
|
||||
}
|
||||
mWorkerScope.updateRAList(pages);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -431,7 +423,7 @@ public final class SpiderQueen implements Runnable {
|
||||
* Float for download percent<br>
|
||||
* null for wait
|
||||
*/
|
||||
private Object request(int index, boolean ignoreError, boolean force, boolean addNeighbor) {
|
||||
private Object request(int index, boolean ignoreError, boolean force) {
|
||||
if (mQueenThread == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -447,10 +439,7 @@ public final class SpiderQueen implements Runnable {
|
||||
state = STATE_NONE;
|
||||
}
|
||||
|
||||
mWorkerScope.launchIndexed(index, force);
|
||||
for (int i = index + 1, n = index + 1 + mPreloadNumber; i < n && i < mPageStateArray.length; i++) {
|
||||
mWorkerScope.launchIndexed(i);
|
||||
}
|
||||
mWorkerScope.launch(index, force);
|
||||
|
||||
Object result;
|
||||
|
||||
@@ -797,6 +786,7 @@ public final class SpiderQueen implements Runnable {
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface State {
|
||||
}
|
||||
|
||||
public interface OnSpiderListener {
|
||||
|
||||
void onGetPages(int pages);
|
||||
@@ -873,7 +863,7 @@ public final class SpiderQueen implements Runnable {
|
||||
// Can't find the file, it might be removed from cache,
|
||||
// Reset it state and request it
|
||||
updatePageState(index, STATE_NONE, null);
|
||||
request(index, false, false, false);
|
||||
request(index, false, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,200 +42,223 @@ class SpiderQueenWorker(private val queen: SpiderQueen) : CoroutineScope {
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = Dispatchers.IO.limitedParallelism(maxParallelismSize) + SupervisorJob()
|
||||
|
||||
fun removeRequested(index: Int) {
|
||||
mJobMap[index]?.cancel()
|
||||
fun cancel(index: Int) {
|
||||
synchronized(mJobMap) {
|
||||
mJobMap.remove(index)?.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
fun updateRAList(list: List<Int>) {
|
||||
synchronized(mJobMap) {
|
||||
sequence {
|
||||
mJobMap.forEach { (i, job) ->
|
||||
if (i !in list) {
|
||||
job.cancel()
|
||||
}
|
||||
if (!job.isActive) yield(i)
|
||||
}
|
||||
}.toSet().forEach { mJobMap.remove(it) }
|
||||
list.forEach {
|
||||
if (mJobMap[it]?.isActive != true)
|
||||
launch(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
@Synchronized
|
||||
fun launchIndexed(index: Int, force: Boolean = false) {
|
||||
fun launch(index: Int, force: Boolean = false) {
|
||||
check(index in 0 until size)
|
||||
val state = queen.mPageStateArray[index]
|
||||
val currentJob = mJobMap[index]
|
||||
if (!force && state == STATE_FINISHED) return
|
||||
if (force) currentJob?.cancel()
|
||||
if (currentJob?.isActive != true) {
|
||||
mJobMap[index] = launch {
|
||||
fun getPToken(index: Int): String? {
|
||||
if (index !in 0 until size) return null
|
||||
return spiderInfo.pTokenMap[index].takeIf { it != TOKEN_FAILED }
|
||||
?: queen.getPTokenFromInternet(index)
|
||||
?: queen.getPTokenFromInternet(index)
|
||||
?: queen.getPTokenFromMultiPageViewer(index)
|
||||
}
|
||||
queen.updatePageState(index, SpiderQueen.STATE_DOWNLOADING)
|
||||
if (!force && index in spiderDen) {
|
||||
queen.updatePageState(index, STATE_FINISHED)
|
||||
return@launch
|
||||
}
|
||||
if (force) {
|
||||
synchronized(pTokenLock) {
|
||||
val pToken = spiderInfo.pTokenMap[index]
|
||||
if (pToken == TOKEN_FAILED) spiderInfo.pTokenMap.remove(index)
|
||||
}
|
||||
}
|
||||
var pToken: String?
|
||||
var previousPToken: String?
|
||||
|
||||
currentCoroutineContext().ensureActive()
|
||||
synchronized(pTokenLock) {
|
||||
pToken = getPToken(index)
|
||||
if (pToken == null) {
|
||||
spiderInfo.pTokenMap[index] = TOKEN_FAILED
|
||||
queen.updatePageState(index, STATE_FAILED, PTOKEN_FAILED_MESSAGE)
|
||||
return@launch
|
||||
}
|
||||
previousPToken = getPToken(index - 1)
|
||||
}
|
||||
|
||||
var skipHathKey: String? = null
|
||||
val skipHathKeys = mutableListOf<String>()
|
||||
var originImageUrl: String? = null
|
||||
val pageUrl: String? = null
|
||||
var error: String? = null
|
||||
var forceHtml = false
|
||||
var leakSkipHathKey = false
|
||||
repeat(3) {
|
||||
var imageUrl: String? = null
|
||||
var localShowKey: String?
|
||||
|
||||
currentCoroutineContext().ensureActive()
|
||||
synchronized(showKeyLock) {
|
||||
localShowKey = showKey
|
||||
if (localShowKey == null || forceHtml) {
|
||||
if (leakSkipHathKey) return@repeat
|
||||
val pageUrl1 =
|
||||
getPageUrl(spiderInfo.gid, index, pToken!!, pageUrl, skipHathKey)
|
||||
runCatching {
|
||||
fetchPageResultFromHtml(index, pageUrl1)
|
||||
}.onSuccess {
|
||||
imageUrl = it.imageUrl
|
||||
skipHathKey = it.skipHathKey
|
||||
originImageUrl = it.originImageUrl
|
||||
localShowKey = it.showKey
|
||||
|
||||
if (!skipHathKey.isNullOrBlank()) {
|
||||
if (skipHathKeys.contains(skipHathKey)) {
|
||||
// Duplicate skip hath key
|
||||
leakSkipHathKey = true
|
||||
} else {
|
||||
skipHathKeys.add(skipHathKey!!)
|
||||
}
|
||||
} else {
|
||||
leakSkipHathKey = true
|
||||
}
|
||||
|
||||
showKey = it.showKey
|
||||
}.onFailure {
|
||||
if (it is Image509Exception) {
|
||||
error = ERROR_509
|
||||
return@repeat
|
||||
}
|
||||
if (it is ParseException && "Key mismatch" == it.message) {
|
||||
// Show key is wrong, enter a new loop to get the new show key
|
||||
if (showKey == localShowKey) showKey = null
|
||||
return@repeat
|
||||
} else {
|
||||
error = ExceptionUtils.getReadableString(it)
|
||||
return@repeat
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentCoroutineContext().ensureActive()
|
||||
if (imageUrl == null) {
|
||||
if (localShowKey == null) {
|
||||
error = "ShowKey error"
|
||||
return@repeat
|
||||
}
|
||||
runCatching {
|
||||
fetchPageResultFromApi(
|
||||
spiderInfo.gid,
|
||||
index,
|
||||
pToken,
|
||||
localShowKey,
|
||||
previousPToken
|
||||
)
|
||||
}.onFailure {
|
||||
if (it is Image509Exception) {
|
||||
error = ERROR_509
|
||||
return@repeat
|
||||
}
|
||||
if (it is ParseException && "Key mismatch" == it.message) {
|
||||
// Show key is wrong, enter a new loop to get the new show key
|
||||
if (showKey == localShowKey) showKey = null
|
||||
return@repeat
|
||||
} else {
|
||||
error = ExceptionUtils.getReadableString(it)
|
||||
return@repeat
|
||||
}
|
||||
}.onSuccess {
|
||||
imageUrl = it.imageUrl
|
||||
skipHathKey = it.skipHathKey
|
||||
originImageUrl = it.originImageUrl
|
||||
}
|
||||
}
|
||||
|
||||
val targetImageUrl: String?
|
||||
val referer: String?
|
||||
|
||||
if (Settings.getDownloadOriginImage() && !originImageUrl.isNullOrBlank()) {
|
||||
targetImageUrl = originImageUrl
|
||||
referer = getPageUrl(spiderInfo.gid, index, pToken!!)
|
||||
} else {
|
||||
targetImageUrl = imageUrl
|
||||
referer = null
|
||||
}
|
||||
if (targetImageUrl == null) {
|
||||
error = "TargetImageUrl error"
|
||||
return@repeat
|
||||
}
|
||||
Log.d(DEBUG_TAG, targetImageUrl)
|
||||
|
||||
currentCoroutineContext().ensureActive()
|
||||
runCatching {
|
||||
Log.d(DEBUG_TAG, "Start download image $index")
|
||||
val success: Boolean = spiderDen.makeHttpCallAndSaveImage(
|
||||
index,
|
||||
targetImageUrl,
|
||||
referer
|
||||
) { contentLength: Long, receivedSize: Long, bytesRead: Int ->
|
||||
queen.mPagePercentMap[index] = receivedSize.toFloat() / contentLength
|
||||
queen.notifyPageDownload(index, contentLength, receivedSize, bytesRead)
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
Log.e(DEBUG_TAG, "Can't download all of image data")
|
||||
error = "Incomplete"
|
||||
forceHtml = true
|
||||
return@repeat
|
||||
}
|
||||
|
||||
if (spiderDen.checkPlainText(index)) {
|
||||
error = ""
|
||||
forceHtml = true
|
||||
return@repeat
|
||||
}
|
||||
|
||||
Log.d(DEBUG_TAG, "Download image succeed $index")
|
||||
|
||||
queen.updatePageState(index, STATE_FINISHED)
|
||||
delay(mDownloadDelay.toLong())
|
||||
return@launch
|
||||
}.onFailure {
|
||||
if (it is CancellationException) throw it
|
||||
it.printStackTrace()
|
||||
error = NETWORK_ERROR
|
||||
}
|
||||
Log.d(DEBUG_TAG, "End download image $index")
|
||||
}
|
||||
spiderDen.remove(index)
|
||||
queen.updatePageState(index, STATE_FAILED, error)
|
||||
synchronized(mJobMap) {
|
||||
val currentJob = mJobMap[index]
|
||||
if (force) currentJob?.cancel()
|
||||
if (currentJob?.isActive != true) {
|
||||
mJobMap[index] = launch { doInJob(index, force) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun doInJob(index: Int, force: Boolean) {
|
||||
fun getPToken(index: Int): String? {
|
||||
if (index !in 0 until size) return null
|
||||
return spiderInfo.pTokenMap[index].takeIf { it != TOKEN_FAILED }
|
||||
?: queen.getPTokenFromInternet(index)
|
||||
?: queen.getPTokenFromInternet(index)
|
||||
?: queen.getPTokenFromMultiPageViewer(index)
|
||||
}
|
||||
queen.updatePageState(index, SpiderQueen.STATE_DOWNLOADING)
|
||||
if (!force && index in spiderDen) {
|
||||
queen.updatePageState(index, STATE_FINISHED)
|
||||
return
|
||||
}
|
||||
if (force) {
|
||||
synchronized(pTokenLock) {
|
||||
val pToken = spiderInfo.pTokenMap[index]
|
||||
if (pToken == TOKEN_FAILED) spiderInfo.pTokenMap.remove(index)
|
||||
}
|
||||
}
|
||||
var pToken: String?
|
||||
var previousPToken: String?
|
||||
|
||||
currentCoroutineContext().ensureActive()
|
||||
synchronized(pTokenLock) {
|
||||
pToken = getPToken(index)
|
||||
if (pToken == null) {
|
||||
spiderInfo.pTokenMap[index] = TOKEN_FAILED
|
||||
queen.updatePageState(index, STATE_FAILED, PTOKEN_FAILED_MESSAGE)
|
||||
return
|
||||
}
|
||||
previousPToken = getPToken(index - 1)
|
||||
}
|
||||
|
||||
var skipHathKey: String? = null
|
||||
val skipHathKeys = mutableListOf<String>()
|
||||
var originImageUrl: String? = null
|
||||
val pageUrl: String? = null
|
||||
var error: String? = null
|
||||
var forceHtml = false
|
||||
var leakSkipHathKey = false
|
||||
repeat(3) {
|
||||
var imageUrl: String? = null
|
||||
var localShowKey: String?
|
||||
|
||||
currentCoroutineContext().ensureActive()
|
||||
synchronized(showKeyLock) {
|
||||
localShowKey = showKey
|
||||
if (localShowKey == null || forceHtml) {
|
||||
if (leakSkipHathKey) return@repeat
|
||||
val pageUrl1 =
|
||||
getPageUrl(spiderInfo.gid, index, pToken!!, pageUrl, skipHathKey)
|
||||
runCatching {
|
||||
fetchPageResultFromHtml(index, pageUrl1)
|
||||
}.onSuccess {
|
||||
imageUrl = it.imageUrl
|
||||
skipHathKey = it.skipHathKey
|
||||
originImageUrl = it.originImageUrl
|
||||
localShowKey = it.showKey
|
||||
|
||||
if (!skipHathKey.isNullOrBlank()) {
|
||||
if (skipHathKeys.contains(skipHathKey)) {
|
||||
// Duplicate skip hath key
|
||||
leakSkipHathKey = true
|
||||
} else {
|
||||
skipHathKeys.add(skipHathKey!!)
|
||||
}
|
||||
} else {
|
||||
leakSkipHathKey = true
|
||||
}
|
||||
|
||||
showKey = it.showKey
|
||||
}.onFailure {
|
||||
if (it is Image509Exception) {
|
||||
error = ERROR_509
|
||||
return@repeat
|
||||
}
|
||||
if (it is ParseException && "Key mismatch" == it.message) {
|
||||
// Show key is wrong, enter a new loop to get the new show key
|
||||
if (showKey == localShowKey) showKey = null
|
||||
return@repeat
|
||||
} else {
|
||||
error = ExceptionUtils.getReadableString(it)
|
||||
return@repeat
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentCoroutineContext().ensureActive()
|
||||
if (imageUrl == null) {
|
||||
if (localShowKey == null) {
|
||||
error = "ShowKey error"
|
||||
return@repeat
|
||||
}
|
||||
runCatching {
|
||||
fetchPageResultFromApi(
|
||||
spiderInfo.gid,
|
||||
index,
|
||||
pToken,
|
||||
localShowKey,
|
||||
previousPToken
|
||||
)
|
||||
}.onFailure {
|
||||
if (it is Image509Exception) {
|
||||
error = ERROR_509
|
||||
return@repeat
|
||||
}
|
||||
if (it is ParseException && "Key mismatch" == it.message) {
|
||||
// Show key is wrong, enter a new loop to get the new show key
|
||||
if (showKey == localShowKey) showKey = null
|
||||
return@repeat
|
||||
} else {
|
||||
error = ExceptionUtils.getReadableString(it)
|
||||
return@repeat
|
||||
}
|
||||
}.onSuccess {
|
||||
imageUrl = it.imageUrl
|
||||
skipHathKey = it.skipHathKey
|
||||
originImageUrl = it.originImageUrl
|
||||
}
|
||||
}
|
||||
|
||||
val targetImageUrl: String?
|
||||
val referer: String?
|
||||
|
||||
if (Settings.getDownloadOriginImage() && !originImageUrl.isNullOrBlank()) {
|
||||
targetImageUrl = originImageUrl
|
||||
referer = getPageUrl(spiderInfo.gid, index, pToken!!)
|
||||
} else {
|
||||
targetImageUrl = imageUrl
|
||||
referer = null
|
||||
}
|
||||
if (targetImageUrl == null) {
|
||||
error = "TargetImageUrl error"
|
||||
return@repeat
|
||||
}
|
||||
Log.d(DEBUG_TAG, targetImageUrl)
|
||||
|
||||
currentCoroutineContext().ensureActive()
|
||||
runCatching {
|
||||
Log.d(DEBUG_TAG, "Start download image $index")
|
||||
val success: Boolean = spiderDen.makeHttpCallAndSaveImage(
|
||||
index,
|
||||
targetImageUrl,
|
||||
referer
|
||||
) { contentLength: Long, receivedSize: Long, bytesRead: Int ->
|
||||
queen.mPagePercentMap[index] = receivedSize.toFloat() / contentLength
|
||||
queen.notifyPageDownload(index, contentLength, receivedSize, bytesRead)
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
Log.e(DEBUG_TAG, "Can't download all of image data")
|
||||
error = "Incomplete"
|
||||
forceHtml = true
|
||||
return@repeat
|
||||
}
|
||||
|
||||
if (spiderDen.checkPlainText(index)) {
|
||||
error = ""
|
||||
forceHtml = true
|
||||
return@repeat
|
||||
}
|
||||
|
||||
Log.d(DEBUG_TAG, "Download image succeed $index")
|
||||
|
||||
queen.updatePageState(index, STATE_FINISHED)
|
||||
delay(mDownloadDelay.toLong())
|
||||
return
|
||||
}.onFailure {
|
||||
if (it is CancellationException) throw it
|
||||
it.printStackTrace()
|
||||
error = NETWORK_ERROR
|
||||
}
|
||||
Log.d(DEBUG_TAG, "End download image $index")
|
||||
}
|
||||
spiderDen.remove(index)
|
||||
queen.updatePageState(index, STATE_FAILED, error)
|
||||
}
|
||||
|
||||
private fun getPageUrl(
|
||||
gid: Long, index: Int, pToken: String,
|
||||
oldPageUrl: String?, skipHathKey: String?
|
||||
|
||||
Reference in new issue
Block a user