Add throttling when restoring E-Hentai/ExHentai galleries.
This commit is contained in:
parent
6ada3cbf59
commit
39e0d434ad
@ -30,8 +30,10 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault
|
|||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
|
import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||||
import eu.kanade.tachiyomi.util.sendLocalBroadcast
|
import eu.kanade.tachiyomi.util.sendLocalBroadcast
|
||||||
import eu.kanade.tachiyomi.util.syncChaptersWithSource
|
import eu.kanade.tachiyomi.util.syncChaptersWithSource
|
||||||
|
import exh.eh.EHentaiThrottleManager
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
@ -280,9 +282,12 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
|
|||||||
* @param manga manga that needs updating
|
* @param manga manga that needs updating
|
||||||
* @return [Observable] that contains manga
|
* @return [Observable] that contains manga
|
||||||
*/
|
*/
|
||||||
fun restoreChapterFetchObservable(source: Source, manga: Manga, chapters: List<Chapter>): Observable<Pair<List<Chapter>, List<Chapter>>> {
|
fun restoreChapterFetchObservable(source: Source, manga: Manga, chapters: List<Chapter>, throttleManager: EHentaiThrottleManager): Observable<Pair<List<Chapter>, List<Chapter>>> {
|
||||||
return source.fetchChapterList(manga)
|
return (if(source is EHentai) {
|
||||||
.map { syncChaptersWithSource(databaseHelper, it, manga, source) }
|
source.fetchChapterList(manga, throttleManager::throttle)
|
||||||
|
} else {
|
||||||
|
source.fetchChapterList(manga)
|
||||||
|
}).map { syncChaptersWithSource(databaseHelper, it, manga, source) }
|
||||||
.doOnNext {
|
.doOnNext {
|
||||||
if (it.first.isNotEmpty()) {
|
if (it.first.isNotEmpty()) {
|
||||||
chapters.forEach { it.manga_id = manga.id }
|
chapters.forEach { it.manga_id = manga.id }
|
||||||
|
@ -28,11 +28,14 @@ import eu.kanade.tachiyomi.util.chop
|
|||||||
import eu.kanade.tachiyomi.util.isServiceRunning
|
import eu.kanade.tachiyomi.util.isServiceRunning
|
||||||
import eu.kanade.tachiyomi.util.sendLocalBroadcast
|
import eu.kanade.tachiyomi.util.sendLocalBroadcast
|
||||||
import exh.BackupEntry
|
import exh.BackupEntry
|
||||||
|
import exh.EH_SOURCE_ID
|
||||||
import exh.EXHMigrations
|
import exh.EXHMigrations
|
||||||
|
import exh.EXH_SOURCE_ID
|
||||||
|
import exh.eh.EHentaiThrottleManager
|
||||||
|
import exh.eh.EHentaiUpdateWorker
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import rx.schedulers.Schedulers
|
import rx.schedulers.Schedulers
|
||||||
import timber.log.Timber
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
@ -125,6 +128,8 @@ class BackupRestoreService : Service() {
|
|||||||
|
|
||||||
private lateinit var executor: ExecutorService
|
private lateinit var executor: ExecutorService
|
||||||
|
|
||||||
|
private val throttleManager = EHentaiThrottleManager()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method called when the service is created. It injects dependencies and acquire the wake lock.
|
* Method called when the service is created. It injects dependencies and acquire the wake lock.
|
||||||
*/
|
*/
|
||||||
@ -167,13 +172,23 @@ class BackupRestoreService : Service() {
|
|||||||
|
|
||||||
val uri = intent.getParcelableExtra<Uri>(BackupConst.EXTRA_URI)
|
val uri = intent.getParcelableExtra<Uri>(BackupConst.EXTRA_URI)
|
||||||
|
|
||||||
|
throttleManager.resetThrottle()
|
||||||
|
|
||||||
// Unsubscribe from any previous subscription if needed.
|
// Unsubscribe from any previous subscription if needed.
|
||||||
subscription?.unsubscribe()
|
subscription?.unsubscribe()
|
||||||
|
|
||||||
subscription = Observable.using(
|
subscription = Observable.using(
|
||||||
{ db.lowLevel().beginTransaction() },
|
{
|
||||||
|
// Pause auto-gallery-update during restore
|
||||||
|
EHentaiUpdateWorker.cancelBackground(this)
|
||||||
|
db.lowLevel().beginTransaction()
|
||||||
|
},
|
||||||
{ getRestoreObservable(uri).doOnNext { db.lowLevel().setTransactionSuccessful() } },
|
{ getRestoreObservable(uri).doOnNext { db.lowLevel().setTransactionSuccessful() } },
|
||||||
{ executor.execute { db.lowLevel().endTransaction() } })
|
{
|
||||||
|
// Resume auto-gallery-update
|
||||||
|
EHentaiUpdateWorker.scheduleBackground(this)
|
||||||
|
executor.execute { db.lowLevel().endTransaction() }
|
||||||
|
})
|
||||||
.doAfterTerminate { stopSelf(startId) }
|
.doAfterTerminate { stopSelf(startId) }
|
||||||
.subscribeOn(Schedulers.from(executor))
|
.subscribeOn(Schedulers.from(executor))
|
||||||
.subscribe()
|
.subscribe()
|
||||||
@ -340,6 +355,9 @@ class BackupRestoreService : Service() {
|
|||||||
private fun mangaFetchObservable(source: Source, manga: Manga, chapters: List<Chapter>,
|
private fun mangaFetchObservable(source: Source, manga: Manga, chapters: List<Chapter>,
|
||||||
categories: List<String>, history: List<DHistory>,
|
categories: List<String>, history: List<DHistory>,
|
||||||
tracks: List<Track>): Observable<Manga> {
|
tracks: List<Track>): Observable<Manga> {
|
||||||
|
if(source.id == EH_SOURCE_ID || source.id == EXH_SOURCE_ID)
|
||||||
|
throttleManager.throttle()
|
||||||
|
|
||||||
return backupManager.restoreMangaFetchObservable(source, manga)
|
return backupManager.restoreMangaFetchObservable(source, manga)
|
||||||
.onErrorReturn {
|
.onErrorReturn {
|
||||||
// [EXH]
|
// [EXH]
|
||||||
@ -419,7 +437,7 @@ class BackupRestoreService : Service() {
|
|||||||
* @return [Observable] that contains manga
|
* @return [Observable] that contains manga
|
||||||
*/
|
*/
|
||||||
private fun chapterFetchObservable(source: Source, manga: Manga, chapters: List<Chapter>): Observable<Pair<List<Chapter>, List<Chapter>>> {
|
private fun chapterFetchObservable(source: Source, manga: Manga, chapters: List<Chapter>): Observable<Pair<List<Chapter>, List<Chapter>>> {
|
||||||
return backupManager.restoreChapterFetchObservable(source, manga, chapters)
|
return backupManager.restoreChapterFetchObservable(source, manga, chapters, throttleManager)
|
||||||
// If there's any error, return empty update and continue.
|
// If there's any error, return empty update and continue.
|
||||||
.onErrorReturn {
|
.onErrorReturn {
|
||||||
// [EXH]
|
// [EXH]
|
||||||
|
@ -305,7 +305,7 @@ class SettingsBackupController : SettingsController() {
|
|||||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||||
return MaterialDialog.Builder(activity!!)
|
return MaterialDialog.Builder(activity!!)
|
||||||
.title(R.string.pref_restore_backup)
|
.title(R.string.pref_restore_backup)
|
||||||
.content(R.string.backup_restore_content)
|
.content(activity!!.getString(R.string.backup_restore_content) + "\n\nThe app will throttle when restoring EHentai/ExHentai backups. This may cause the app to appear frozen when it is actually still working. Report an issue if the app remains frozen for more than 30 minutes however.")
|
||||||
.positiveText(R.string.action_restore)
|
.positiveText(R.string.action_restore)
|
||||||
.onPositive { _, _ ->
|
.onPositive { _, _ ->
|
||||||
val context = applicationContext
|
val context = applicationContext
|
||||||
|
@ -176,7 +176,12 @@ class GalleryAdder {
|
|||||||
|
|
||||||
//Fetch and copy chapters
|
//Fetch and copy chapters
|
||||||
try {
|
try {
|
||||||
sourceObj.fetchChapterList(manga).map {
|
val chapterListObs = if(sourceObj is EHentai) {
|
||||||
|
sourceObj.fetchChapterList(manga, throttleFunc)
|
||||||
|
} else {
|
||||||
|
sourceObj.fetchChapterList(manga)
|
||||||
|
}
|
||||||
|
chapterListObs.map {
|
||||||
syncChaptersWithSource(db, it, manga, sourceObj)
|
syncChaptersWithSource(db, it, manga, sourceObj)
|
||||||
}.toBlocking().first()
|
}.toBlocking().first()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
31
app/src/main/java/exh/eh/EHentaiThrottleManager.kt
Normal file
31
app/src/main/java/exh/eh/EHentaiThrottleManager.kt
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package exh.eh
|
||||||
|
|
||||||
|
class EHentaiThrottleManager(private val max: Int = THROTTLE_MAX,
|
||||||
|
private val inc: Int = THROTTLE_INC) {
|
||||||
|
private var lastThrottleTime: Long = 0
|
||||||
|
var throttleTime: Long = 0
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun throttle() {
|
||||||
|
//Throttle requests if necessary
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
val timeDiff = now - lastThrottleTime
|
||||||
|
if(timeDiff < throttleTime)
|
||||||
|
Thread.sleep(throttleTime - timeDiff)
|
||||||
|
|
||||||
|
if(throttleTime < max)
|
||||||
|
throttleTime += inc
|
||||||
|
|
||||||
|
lastThrottleTime = System.currentTimeMillis()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetThrottle() {
|
||||||
|
lastThrottleTime = 0
|
||||||
|
throttleTime = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val THROTTLE_MAX = 5500
|
||||||
|
const val THROTTLE_INC = 20
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,7 @@ import eu.kanade.tachiyomi.util.powerManager
|
|||||||
import eu.kanade.tachiyomi.util.toast
|
import eu.kanade.tachiyomi.util.toast
|
||||||
import eu.kanade.tachiyomi.util.wifiManager
|
import eu.kanade.tachiyomi.util.wifiManager
|
||||||
import exh.*
|
import exh.*
|
||||||
|
import exh.eh.EHentaiThrottleManager
|
||||||
import exh.eh.EHentaiUpdateWorker
|
import exh.eh.EHentaiUpdateWorker
|
||||||
import exh.util.ignore
|
import exh.util.ignore
|
||||||
import exh.util.trans
|
import exh.util.trans
|
||||||
@ -42,8 +43,7 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
|
|
||||||
private val galleryAdder = GalleryAdder()
|
private val galleryAdder = GalleryAdder()
|
||||||
|
|
||||||
private var lastThrottleTime: Long = 0
|
private val throttleManager = EHentaiThrottleManager()
|
||||||
private var throttleTime: Long = 0
|
|
||||||
|
|
||||||
private var wifiLock: WifiManager.WifiLock? = null
|
private var wifiLock: WifiManager.WifiLock? = null
|
||||||
private var wakeLock: PowerManager.WakeLock? = null
|
private var wakeLock: PowerManager.WakeLock? = null
|
||||||
@ -294,12 +294,12 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Apply additions
|
//Apply additions
|
||||||
resetThrottle()
|
throttleManager.resetThrottle()
|
||||||
changeSet.added.forEachIndexed { index, it ->
|
changeSet.added.forEachIndexed { index, it ->
|
||||||
status.onNext(FavoritesSyncStatus.Processing("Adding gallery ${index + 1} of ${changeSet.added.size} to remote server",
|
status.onNext(FavoritesSyncStatus.Processing("Adding gallery ${index + 1} of ${changeSet.added.size} to remote server",
|
||||||
needWarnThrottle()))
|
needWarnThrottle()))
|
||||||
|
|
||||||
throttle()
|
throttleManager.throttle()
|
||||||
|
|
||||||
addGalleryRemote(errorList, it)
|
addGalleryRemote(errorList, it)
|
||||||
}
|
}
|
||||||
@ -335,18 +335,18 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
val categories = db.getCategories().executeAsBlocking()
|
val categories = db.getCategories().executeAsBlocking()
|
||||||
|
|
||||||
//Apply additions
|
//Apply additions
|
||||||
resetThrottle()
|
throttleManager.resetThrottle()
|
||||||
changeSet.added.forEachIndexed { index, it ->
|
changeSet.added.forEachIndexed { index, it ->
|
||||||
status.onNext(FavoritesSyncStatus.Processing("Adding gallery ${index + 1} of ${changeSet.added.size} to local library",
|
status.onNext(FavoritesSyncStatus.Processing("Adding gallery ${index + 1} of ${changeSet.added.size} to local library",
|
||||||
needWarnThrottle()))
|
needWarnThrottle()))
|
||||||
|
|
||||||
throttle()
|
throttleManager.throttle()
|
||||||
|
|
||||||
//Import using gallery adder
|
//Import using gallery adder
|
||||||
val result = galleryAdder.addGallery("${exh.baseUrl}${it.getUrl()}",
|
val result = galleryAdder.addGallery("${exh.baseUrl}${it.getUrl()}",
|
||||||
true,
|
true,
|
||||||
EXH_SOURCE_ID,
|
EXH_SOURCE_ID,
|
||||||
::throttle)
|
throttleManager::throttle)
|
||||||
|
|
||||||
if(result is GalleryAddEvent.Fail) {
|
if(result is GalleryAddEvent.Fail) {
|
||||||
if(result is GalleryAddEvent.Fail.NotFound) {
|
if(result is GalleryAddEvent.Fail.NotFound) {
|
||||||
@ -380,32 +380,12 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun throttle() {
|
|
||||||
//Throttle requests if necessary
|
|
||||||
val now = System.currentTimeMillis()
|
|
||||||
val timeDiff = now - lastThrottleTime
|
|
||||||
if(timeDiff < throttleTime)
|
|
||||||
Thread.sleep(throttleTime - timeDiff)
|
|
||||||
|
|
||||||
if(throttleTime < THROTTLE_MAX)
|
|
||||||
throttleTime += THROTTLE_INC
|
|
||||||
|
|
||||||
lastThrottleTime = System.currentTimeMillis()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun resetThrottle() {
|
|
||||||
lastThrottleTime = 0
|
|
||||||
throttleTime = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
fun needWarnThrottle()
|
fun needWarnThrottle()
|
||||||
= throttleTime >= THROTTLE_WARN
|
= throttleManager.throttleTime >= THROTTLE_WARN
|
||||||
|
|
||||||
class IgnoredException : RuntimeException()
|
class IgnoredException : RuntimeException()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val THROTTLE_MAX = 5500
|
|
||||||
private const val THROTTLE_INC = 20
|
|
||||||
private const val THROTTLE_WARN = 1000
|
private const val THROTTLE_WARN = 1000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user