Ignore non-existent galleries during favorites sync

This commit is contained in:
NerdNumber9 2019-04-14 18:53:34 -04:00
parent 98ac8a69c2
commit 77c07d13c0
5 changed files with 47 additions and 20 deletions

View File

@ -10,7 +10,11 @@ import rx.Producer
import rx.Subscription import rx.Subscription
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
fun Call.asObservable(): Observable<Response> { fun Call.asObservableWithAsyncStacktrace(): Observable<Pair<Exception, Response>> {
// Record stacktrace at creation time for easier debugging
// asObservable is involved in a lot of crashes so this is worth the performance hit
val asyncStackTrace = Exception("Async stacktrace")
return Observable.unsafeCreate { subscriber -> return Observable.unsafeCreate { subscriber ->
// Since Call is a one-shot type, clone it for each new subscriber. // Since Call is a one-shot type, clone it for each new subscriber.
val call = clone() val call = clone()
@ -23,12 +27,12 @@ fun Call.asObservable(): Observable<Response> {
try { try {
val response = call.execute() val response = call.execute()
if (!subscriber.isUnsubscribed) { if (!subscriber.isUnsubscribed) {
subscriber.onNext(response) subscriber.onNext(asyncStackTrace to response)
subscriber.onCompleted() subscriber.onCompleted()
} }
} catch (error: Exception) { } catch (error: Exception) {
if (!subscriber.isUnsubscribed) { if (!subscriber.isUnsubscribed) {
subscriber.onError(error) subscriber.onError(error.withRootCause(asyncStackTrace))
} }
} }
} }
@ -47,19 +51,14 @@ fun Call.asObservable(): Observable<Response> {
} }
} }
fun Call.asObservableSuccess(): Observable<Response> { fun Call.asObservable() = asObservableWithAsyncStacktrace().map { it.second }
// Record stacktrace at creation time for easier debugging
// asObservable is involved in a lot of crashes so this is worth the performance hit
val asyncStackTrace = Exception("Async stacktrace")
return asObservable().doOnNext { response -> fun Call.asObservableSuccess(): Observable<Response> {
return asObservableWithAsyncStacktrace().map { (asyncStacktrace, response) ->
if (!response.isSuccessful) { if (!response.isSuccessful) {
response.close() response.close()
throw Exception("HTTP error ${response.code()}") throw Exception("HTTP error ${response.code()}", asyncStacktrace)
} } else response
}.onErrorReturn {
// Set root cause to async stacktrace and throw again
throw it.withRootCause(asyncStackTrace)
} }
} }

View File

@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.network.asObservableWithAsyncStacktrace
import eu.kanade.tachiyomi.source.model.* import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.LewdSource
@ -32,6 +33,7 @@ import uy.kohesive.injekt.injectLazy
import java.net.URLEncoder import java.net.URLEncoder
import java.util.* import java.util.*
import exh.metadata.metadata.base.RaisedTag import exh.metadata.metadata.base.RaisedTag
import java.lang.RuntimeException
class EHentai(override val id: Long, class EHentai(override val id: Long,
val exh: Boolean, val exh: Boolean,
@ -236,11 +238,21 @@ class EHentai(override val id: Long,
*/ */
override fun fetchMangaDetails(manga: SManga): Observable<SManga> { override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return client.newCall(mangaDetailsRequest(manga)) return client.newCall(mangaDetailsRequest(manga))
.asObservableSuccess() .asObservableWithAsyncStacktrace()
.flatMap { .flatMap { (stacktrace, response) ->
parseToManga(manga, it).andThen(Observable.just(manga.apply { if(response.isSuccessful) {
initialized = true parseToManga(manga, response).andThen(Observable.just(manga.apply {
})) initialized = true
}))
} else {
response.close()
if(response.code() == 404) {
throw GalleryNotFoundException(stacktrace)
} else {
throw Exception("HTTP error ${response.code()}", stacktrace)
}
}
} }
} }
@ -522,6 +534,8 @@ class EHentai(override val id: Long,
else else
"E-Hentai" "E-Hentai"
class GalleryNotFoundException(cause: Throwable): RuntimeException("Gallery not found!", cause)
companion object { companion object {
private const val QUERY_PREFIX = "?f_apply=Apply+Filter" private const val QUERY_PREFIX = "?f_apply=Apply+Filter"
private const val TR_SUFFIX = "TR" private const val TR_SUFFIX = "TR"

View File

@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
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.syncChaptersWithSource import eu.kanade.tachiyomi.util.syncChaptersWithSource
import exh.metadata.metadata.EHentaiSearchMetadata import exh.metadata.metadata.EHentaiSearchMetadata
import okhttp3.MediaType import okhttp3.MediaType
@ -186,6 +187,11 @@ class GalleryAdder {
return GalleryAddEvent.Success(url, manga) return GalleryAddEvent.Success(url, manga)
} catch(e: Exception) { } catch(e: Exception) {
XLog.w("Could not add gallery!", e) XLog.w("Could not add gallery!", e)
if(e is EHentai.GalleryNotFoundException) {
return GalleryAddEvent.Fail.NotFound(url)
}
return GalleryAddEvent.Fail.Error(url, return GalleryAddEvent.Fail.Error(url,
((e.message ?: "Unknown error!") + " (Gallery: $url)").trim()) ((e.message ?: "Unknown error!") + " (Gallery: $url)").trim())
} }
@ -223,7 +229,10 @@ sealed class GalleryAddEvent {
override val logMessage = "Unknown gallery type for gallery: $galleryUrl" override val logMessage = "Unknown gallery type for gallery: $galleryUrl"
} }
class Error(override val galleryUrl: String, open class Error(override val galleryUrl: String,
override val logMessage: String): Fail() override val logMessage: String): Fail()
class NotFound(galleryUrl: String):
Error(galleryUrl, "Gallery does not exist: $galleryUrl")
} }
} }

View File

@ -323,6 +323,12 @@ class FavoritesSyncHelper(val context: Context) {
EXH_SOURCE_ID) EXH_SOURCE_ID)
if(result is GalleryAddEvent.Fail) { if(result is GalleryAddEvent.Fail) {
if(result is GalleryAddEvent.Fail.NotFound) {
XLog.e("Remote gallery does not exist, skipping: %s!", it.getUrl())
// Skip this gallery, it no longer exists
return@forEachIndexed
}
val errorString = "Failed to add gallery to local database: " + when (result) { val errorString = "Failed to add gallery to local database: " + when (result) {
is GalleryAddEvent.Fail.Error -> "'${it.title}' ${result.logMessage}" is GalleryAddEvent.Fail.Error -> "'${it.title}' ${result.logMessage}"
is GalleryAddEvent.Fail.UnknownType -> "'${it.title}' (${result.galleryUrl}) is not a valid gallery!" is GalleryAddEvent.Fail.UnknownType -> "'${it.title}' (${result.galleryUrl}) is not a valid gallery!"

View File

@ -43,7 +43,6 @@ class BatchAddPresenter: BasePresenter<BatchAddController>() {
eventRelay?.call((when (result) { eventRelay?.call((when (result) {
is GalleryAddEvent.Success -> "[OK]" is GalleryAddEvent.Success -> "[OK]"
is GalleryAddEvent.Fail -> "[ERROR]" is GalleryAddEvent.Fail -> "[ERROR]"
else -> "[???]"
}) + " " + result.logMessage) }) + " " + result.logMessage)
} }