Refactor and cleanup

This commit is contained in:
Jobobby04 2020-12-26 14:22:55 -05:00
parent d3b7f639b5
commit 1a609e557b
30 changed files with 179 additions and 212 deletions

View File

@ -52,13 +52,12 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.online.all.MergedSource import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.util.lang.asObservable import eu.kanade.tachiyomi.util.lang.runAsObservable
import exh.MERGED_SOURCE_ID import exh.MERGED_SOURCE_ID
import exh.eh.EHentaiThrottleManager import exh.eh.EHentaiThrottleManager
import exh.merged.sql.models.MergedMangaReference import exh.merged.sql.models.MergedMangaReference
import exh.savedsearches.JsonSavedSearch import exh.savedsearches.JsonSavedSearch
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@ -317,13 +316,14 @@ class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : Ab
override fun restoreChapterFetchObservable(source: Source, manga: Manga, chapters: List<Chapter>, throttleManager: EHentaiThrottleManager): Observable<Pair<List<Chapter>, List<Chapter>>> { override fun restoreChapterFetchObservable(source: Source, manga: Manga, chapters: List<Chapter>, throttleManager: EHentaiThrottleManager): Observable<Pair<List<Chapter>, List<Chapter>>> {
// SY --> // SY -->
return if (source is MergedSource) { return if (source is MergedSource) {
val syncedChapters = runBlocking { source.fetchChaptersAndSync(manga, false) } runAsObservable({
syncedChapters.onEach { pair -> val syncedChapters = source.fetchChaptersAndSync(manga, false)
if (pair.first.isNotEmpty()) { syncedChapters.first.onEach {
chapters.forEach { it.manga_id = manga.id } it.manga_id = manga.id
updateChapters(chapters)
} }
}.asObservable() updateChapters(syncedChapters.first)
syncedChapters
})
} else { } else {
super.restoreChapterFetchObservable(source, manga, chapters, throttleManager) super.restoreChapterFetchObservable(source, manga, chapters, throttleManager)
} }

View File

@ -30,8 +30,8 @@ import eu.kanade.tachiyomi.ui.library.LibraryGroup
import eu.kanade.tachiyomi.ui.manga.track.TrackItem import eu.kanade.tachiyomi.ui.manga.track.TrackItem
import eu.kanade.tachiyomi.util.chapter.NoChaptersException import eu.kanade.tachiyomi.util.chapter.NoChaptersException
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.lang.asObservable
import eu.kanade.tachiyomi.util.lang.awaitSingle import eu.kanade.tachiyomi.util.lang.awaitSingle
import eu.kanade.tachiyomi.util.lang.runAsObservable
import eu.kanade.tachiyomi.util.prepUpdateCover import eu.kanade.tachiyomi.util.prepUpdateCover
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
import eu.kanade.tachiyomi.util.storage.getUriCompat import eu.kanade.tachiyomi.util.storage.getUriCompat
@ -452,7 +452,7 @@ class LibraryUpdateService(
} }
return ( return (
/* SY --> */ if (source is MergedSource) runBlocking { source.fetchChaptersAndSync(manga, false).asObservable() } /* SY --> */ if (source is MergedSource) runAsObservable({ source.fetchChaptersAndSync(manga, false) })
else /* SY <-- */ source.fetchChapterList(manga) else /* SY <-- */ source.fetchChapterList(manga)
.map { syncChaptersWithSource(db, it, manga, source) } .map { syncChaptersWithSource(db, it, manga, source) }
// SY --> // SY -->
@ -544,8 +544,7 @@ class LibraryUpdateService(
private fun syncFollows(): Observable<LibraryManga> { private fun syncFollows(): Observable<LibraryManga> {
val count = AtomicInteger(0) val count = AtomicInteger(0)
val mangaDex = MdUtil.getEnabledMangaDex(preferences, sourceManager) ?: return Observable.empty() val mangaDex = MdUtil.getEnabledMangaDex(preferences, sourceManager) ?: return Observable.empty()
return mangaDex.fetchAllFollows(true) return runAsObservable({ mangaDex.fetchAllFollows(true) })
.asObservable()
.map { listManga -> .map { listManga ->
listManga.filter { (_, metadata) -> listManga.filter { (_, metadata) ->
metadata.follow_status == FollowStatus.RE_READING.int || metadata.follow_status == FollowStatus.READING.int metadata.follow_status == FollowStatus.RE_READING.int || metadata.follow_status == FollowStatus.READING.int
@ -600,7 +599,7 @@ class LibraryUpdateService(
if (tracker.track?.status == FollowStatus.UNFOLLOWED.int) { if (tracker.track?.status == FollowStatus.UNFOLLOWED.int) {
tracker.track.status = FollowStatus.READING.int tracker.track.status = FollowStatus.READING.int
tracker.service.update(tracker.track) runAsObservable({ tracker.service.update(tracker.track) })
} else Observable.just(null) } else Observable.just(null)
} }
.doOnNext { returnedTracker -> .doOnNext { returnedTracker ->

View File

@ -10,16 +10,15 @@ import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.lang.asObservable import eu.kanade.tachiyomi.util.lang.await
import eu.kanade.tachiyomi.util.lang.runAsObservable
import exh.md.utils.FollowStatus import exh.md.utils.FollowStatus
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
import exh.metadata.metadata.MangaDexSearchMetadata import exh.metadata.metadata.MangaDexSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata import exh.metadata.metadata.base.insertFlatMetadata
import exh.util.floor import exh.util.floor
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import rx.Completable
import rx.Observable import rx.Observable
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -49,52 +48,48 @@ class MdList(private val context: Context, id: Int) : TrackService(id) {
override fun displayScore(track: Track) = track.score.toInt().toString() override fun displayScore(track: Track) = track.score.toInt().toString()
override fun add(track: Track): Observable<Track> { override suspend fun add(track: Track): Track {
return update(track) return update(track)
} }
override fun update(track: Track): Observable<Track> { override suspend fun update(track: Track): Track {
val mdex = mdex ?: throw Exception("Mangadex not enabled") val mdex = mdex ?: throw Exception("Mangadex not enabled")
return Observable.defer { val manga = db.getManga(track.tracking_url.substringAfter(".org"), mdex.id).await() ?: throw Exception("Manga doesnt exist")
db.getManga(track.tracking_url.substringAfter(".org"), mdex.id)
.asRxObservable()
.map { manga ->
val mangaMetadata = db.getFlatMetadataForManga(manga.id!!).executeAsBlocking()?.raise(MangaDexSearchMetadata::class) ?: throw Exception("Invalid manga metadata")
val followStatus = FollowStatus.fromInt(track.status)!!
// allow follow status to update val mangaMetadata = db.getFlatMetadataForManga(manga.id!!).executeAsBlocking()?.raise(MangaDexSearchMetadata::class) ?: throw Exception("Invalid manga metadata")
if (mangaMetadata.follow_status != followStatus.int) { val followStatus = FollowStatus.fromInt(track.status)!!
runBlocking { mdex.updateFollowStatus(MdUtil.getMangaId(track.tracking_url), followStatus).collect() }
mangaMetadata.follow_status = followStatus.int
db.insertFlatMetadata(mangaMetadata.flatten()).await()
}
if (track.score.toInt() > 0) { // allow follow status to update
runBlocking { mdex.updateRating(track).collect() } if (mangaMetadata.follow_status != followStatus.int) {
} mdex.updateFollowStatus(MdUtil.getMangaId(track.tracking_url), followStatus)
mangaMetadata.follow_status = followStatus.int
// mangadex wont update chapters if manga is not follows this prevents unneeded network call db.insertFlatMetadata(mangaMetadata.flatten()).await()
if (followStatus != FollowStatus.UNFOLLOWED) {
if (track.total_chapters != 0 && track.last_chapter_read == track.total_chapters) {
track.status = FollowStatus.COMPLETED.int
}
runBlocking { mdex.updateReadingProgress(track).collect() }
} else if (track.last_chapter_read != 0) {
// When followStatus has been changed to unfollowed 0 out read chapters since dex does
track.last_chapter_read = 0
}
track
}
} }
if (track.score.toInt() > 0) {
mdex.updateRating(track)
}
// mangadex wont update chapters if manga is not follows this prevents unneeded network call
if (followStatus != FollowStatus.UNFOLLOWED) {
if (track.total_chapters != 0 && track.last_chapter_read == track.total_chapters) {
track.status = FollowStatus.COMPLETED.int
}
mdex.updateReadingProgress(track)
} else if (track.last_chapter_read != 0) {
// When followStatus has been changed to unfollowed 0 out read chapters since dex does
track.last_chapter_read = 0
}
return track
} }
override fun getCompletionStatus(): Int = FollowStatus.COMPLETED.int override fun getCompletionStatus(): Int = FollowStatus.COMPLETED.int
override fun bind(track: Track): Observable<Track> { override fun bind(track: Track): Observable<Track> {
val mdex = mdex ?: throw Exception("Mangadex not enabled") val mdex = mdex ?: throw Exception("Mangadex not enabled")
return mdex.fetchTrackingInfo(track.tracking_url).asObservable() return runAsObservable({ mdex.fetchTrackingInfo(track.tracking_url) })
.doOnNext { remoteTrack -> .doOnNext { remoteTrack ->
track.copyPersonalFrom(remoteTrack) track.copyPersonalFrom(remoteTrack)
val manga = db.getManga(track.manga_id).executeAsBlocking() val manga = db.getManga(track.manga_id).executeAsBlocking()
@ -103,13 +98,13 @@ class MdList(private val context: Context, id: Int) : TrackService(id) {
} else { } else {
remoteTrack.total_chapters remoteTrack.total_chapters
} }
update(track) runBlocking { update(track) }
} }
} }
override fun refresh(track: Track): Observable<Track> { override fun refresh(track: Track): Observable<Track> {
val mdex = mdex ?: throw Exception("Mangadex not enabled") val mdex = mdex ?: throw Exception("Mangadex not enabled")
return mdex.fetchTrackingInfo(track.tracking_url).asObservable() return runAsObservable({ mdex.fetchTrackingInfo(track.tracking_url) })
.map { remoteTrack -> .map { remoteTrack ->
track.copyPersonalFrom(remoteTrack) track.copyPersonalFrom(remoteTrack)
val manga = db.getManga(track.manga_id).executeAsBlocking() val manga = db.getManga(track.manga_id).executeAsBlocking()
@ -133,5 +128,5 @@ class MdList(private val context: Context, id: Int) : TrackService(id) {
override fun search(query: String): Observable<List<TrackSearch>> = throw Exception("not used") override fun search(query: String): Observable<List<TrackSearch>> = throw Exception("not used")
override fun login(username: String, password: String): Completable = throw Exception("not used") override suspend fun login(username: String, password: String): Unit = throw Exception("not used")
} }

View File

@ -154,7 +154,7 @@ class ExtensionManager(
} }
// EXH --> // EXH -->
fun <T : Extension> Iterable<T>.filterNotBlacklisted(): List<T> { private fun <T : Extension> Iterable<T>.filterNotBlacklisted(): List<T> {
val blacklistEnabled = preferences.enableSourceBlacklist().get() val blacklistEnabled = preferences.enableSourceBlacklist().get()
return filter { return filter {
if (it.isBlacklisted(blacklistEnabled)) { if (it.isBlacklisted(blacklistEnabled)) {
@ -164,10 +164,7 @@ class ExtensionManager(
} }
} }
fun Extension.isBlacklisted( fun Extension.isBlacklisted(blacklistEnabled: Boolean = preferences.enableSourceBlacklist().get()): Boolean {
blacklistEnabled: Boolean =
preferences.enableSourceBlacklist().get()
): Boolean {
return pkgName in BlacklistedSources.BLACKLISTED_EXTENSIONS && blacklistEnabled return pkgName in BlacklistedSources.BLACKLISTED_EXTENSIONS && blacklistEnabled
} }
// EXH <-- // EXH <--

View File

@ -2,7 +2,8 @@ package eu.kanade.tachiyomi.source.online
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.bluelinelabs.conductor.Controller import com.bluelinelabs.conductor.Controller
import eu.kanade.tachiyomi.source.CatalogueSource
interface BrowseSourceFilterHeader { interface BrowseSourceFilterHeader : CatalogueSource {
fun getFilterHeader(controller: Controller): RecyclerView.Adapter<*> fun getFilterHeader(controller: Controller): RecyclerView.Adapter<*>
} }

View File

@ -1,30 +1,29 @@
package eu.kanade.tachiyomi.source.online package eu.kanade.tachiyomi.source.online
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.md.utils.FollowStatus import exh.md.utils.FollowStatus
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.coroutines.flow.Flow
import rx.Observable
interface FollowsSource { interface FollowsSource : CatalogueSource {
fun fetchFollows(): Observable<MangasPage> suspend fun fetchFollows(): MangasPage
/** /**
* Returns a list of all Follows retrieved by Coroutines * Returns a list of all Follows retrieved by Coroutines
* *
* @param SManga all smanga found for user * @param SManga all smanga found for user
*/ */
fun fetchAllFollows(forceHd: Boolean = false): Flow<List<Pair<SManga, RaisedSearchMetadata>>> suspend fun fetchAllFollows(forceHd: Boolean = false): List<Pair<SManga, RaisedSearchMetadata>>
/** /**
* updates the follow status for a manga * updates the follow status for a manga
*/ */
fun updateFollowStatus(mangaID: String, followStatus: FollowStatus): Flow<Boolean> suspend fun updateFollowStatus(mangaID: String, followStatus: FollowStatus): Boolean
/** /**
* Get a MdList Track of the manga * Get a MdList Track of the manga
*/ */
fun fetchTrackingInfo(url: String): Flow<Track> suspend fun fetchTrackingInfo(url: String): Track
} }

View File

@ -4,7 +4,7 @@ import android.app.Activity
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
interface LoginSource { interface LoginSource : Source {
val needsLogin: Boolean val needsLogin: Boolean
fun isLogged(): Boolean fun isLogged(): Boolean

View File

@ -1,3 +1,5 @@
package eu.kanade.tachiyomi.source.online package eu.kanade.tachiyomi.source.online
interface NamespaceSource import eu.kanade.tachiyomi.source.Source
interface NamespaceSource : Source

View File

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.source.online package eu.kanade.tachiyomi.source.online
import kotlinx.coroutines.flow.Flow import tachiyomi.source.Source
interface RandomMangaSource { interface RandomMangaSource : Source {
fun fetchRandomMangaUrl(): Flow<String> suspend fun fetchRandomMangaUrl(): String
} }

View File

@ -8,10 +8,10 @@ import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.lang.asObservable import eu.kanade.tachiyomi.util.lang.runAsObservable
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import rx.Observable import rx.Observable
@ -29,15 +29,13 @@ abstract class SuspendHttpSource : HttpSource() {
* @param page the page number to retrieve. * @param page the page number to retrieve.
*/ */
final override fun fetchPopularManga(page: Int): Observable<MangasPage> { final override fun fetchPopularManga(page: Int): Observable<MangasPage> {
return fetchPopularMangaFlow(page).asObservable() return runAsObservable({ fetchPopularMangaSuspended(page) })
} }
open fun fetchPopularMangaFlow(page: Int): Flow<MangasPage> { open suspend fun fetchPopularMangaSuspended(page: Int): MangasPage {
return flow { return withContext(Dispatchers.IO) {
val response = client.newCall(popularMangaRequestSuspended(page)).await() val response = client.newCall(popularMangaRequestSuspended(page)).await()
emit( popularMangaParseSuspended(response)
popularMangaParseSuspended(response)
)
} }
} }
@ -72,15 +70,13 @@ abstract class SuspendHttpSource : HttpSource() {
* @param filters the list of filters to apply. * @param filters the list of filters to apply.
*/ */
final override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> { final override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
return fetchSearchMangaSuspended(page, query, filters).asObservable() return runAsObservable({ fetchSearchMangaSuspended(page, query, filters) })
} }
open fun fetchSearchMangaSuspended(page: Int, query: String, filters: FilterList): Flow<MangasPage> { open suspend fun fetchSearchMangaSuspended(page: Int, query: String, filters: FilterList): MangasPage {
return flow { return withContext(Dispatchers.IO) {
val response = client.newCall(searchMangaRequestSuspended(page, query, filters)).await() val response = client.newCall(searchMangaRequestSuspended(page, query, filters)).await()
emit( searchMangaParseSuspended(response)
searchMangaParseSuspended(response)
)
} }
} }
@ -114,15 +110,13 @@ abstract class SuspendHttpSource : HttpSource() {
* @param page the page number to retrieve. * @param page the page number to retrieve.
*/ */
final override fun fetchLatestUpdates(page: Int): Observable<MangasPage> { final override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
return fetchLatestUpdatesFlow(page).asObservable() return runAsObservable({ fetchLatestUpdatesSuspended(page) })
} }
open fun fetchLatestUpdatesFlow(page: Int): Flow<MangasPage> { open suspend fun fetchLatestUpdatesSuspended(page: Int): MangasPage {
return flow { return withContext(Dispatchers.IO) {
val response = client.newCall(latestUpdatesRequestSuspended(page)).await() val response = client.newCall(latestUpdatesRequestSuspended(page)).await()
emit( latestUpdatesParseSuspended(response)
latestUpdatesParseSuspended(response)
)
} }
} }
@ -155,15 +149,13 @@ abstract class SuspendHttpSource : HttpSource() {
* @param manga the manga to be updated. * @param manga the manga to be updated.
*/ */
final override fun fetchMangaDetails(manga: SManga): Observable<SManga> { final override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return fetchMangaDetailsFlow(manga).asObservable() return runAsObservable({ fetchMangaDetailsSuspended(manga) })
} }
open fun fetchMangaDetailsFlow(manga: SManga): Flow<SManga> { open suspend fun fetchMangaDetailsSuspended(manga: SManga): SManga {
return flow { return withContext(Dispatchers.IO) {
val response = client.newCall(mangaDetailsRequestSuspended(manga)).await() val response = client.newCall(mangaDetailsRequestSuspended(manga)).await()
emit( mangaDetailsParseSuspended(response).apply { initialized = true }
mangaDetailsParseSuspended(response).apply { initialized = true }
)
} }
} }
@ -200,20 +192,18 @@ abstract class SuspendHttpSource : HttpSource() {
*/ */
final override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> { final override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
return try { return try {
fetchChapterListFlow(manga).asObservable() runAsObservable({ fetchChapterListSuspended(manga) })
} catch (e: LicencedException) { } catch (e: LicencedException) {
Observable.error(Exception("Licensed - No chapters to show")) Observable.error(Exception("Licensed - No chapters to show"))
} }
} }
@Throws(LicencedException::class) @Throws(LicencedException::class)
open fun fetchChapterListFlow(manga: SManga): Flow<List<SChapter>> { open suspend fun fetchChapterListSuspended(manga: SManga): List<SChapter> {
return flow { return withContext(Dispatchers.IO) {
if (manga.status != SManga.LICENSED) { if (manga.status != SManga.LICENSED) {
val response = client.newCall(chapterListRequestSuspended(manga)).await() val response = client.newCall(chapterListRequestSuspended(manga)).await()
emit( chapterListParseSuspended(response)
chapterListParseSuspended(response)
)
} else { } else {
throw LicencedException("Licensed - No chapters to show") throw LicencedException("Licensed - No chapters to show")
} }
@ -251,15 +241,13 @@ abstract class SuspendHttpSource : HttpSource() {
* @param chapter the chapter whose page list has to be fetched. * @param chapter the chapter whose page list has to be fetched.
*/ */
final override fun fetchPageList(chapter: SChapter): Observable<List<Page>> { final override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
return fetchPageListFlow(chapter).asObservable() return runAsObservable({ fetchPageListSuspended(chapter) })
} }
open fun fetchPageListFlow(chapter: SChapter): Flow<List<Page>> { open suspend fun fetchPageListSuspended(chapter: SChapter): List<Page> {
return flow { return withContext(Dispatchers.IO) {
val response = client.newCall(pageListRequestSuspended(chapter)).await() val response = client.newCall(pageListRequestSuspended(chapter)).await()
emit( pageListParseSuspended(response)
pageListParseSuspended(response)
)
} }
} }
@ -295,15 +283,13 @@ abstract class SuspendHttpSource : HttpSource() {
* @param page the page whose source image has to be fetched. * @param page the page whose source image has to be fetched.
*/ */
final override fun fetchImageUrl(page: Page): Observable<String> { final override fun fetchImageUrl(page: Page): Observable<String> {
return fetchImageUrlFlow(page).asObservable() return runAsObservable({ fetchImageUrlSuspended(page) })
} }
open fun fetchImageUrlFlow(page: Page): Flow<String> { open suspend fun fetchImageUrlSuspended(page: Page): String {
return flow { return withContext(Dispatchers.IO) {
val response = client.newCall(imageUrlRequestSuspended(page)).await() val response = client.newCall(imageUrlRequestSuspended(page)).await()
emit( imageUrlParseSuspended(response)
imageUrlParseSuspended(response)
)
} }
} }
@ -338,14 +324,12 @@ abstract class SuspendHttpSource : HttpSource() {
* @param page the page whose source image has to be downloaded. * @param page the page whose source image has to be downloaded.
*/ */
final override fun fetchImage(page: Page): Observable<Response> { final override fun fetchImage(page: Page): Observable<Response> {
return fetchImageFlow(page).asObservable() return runAsObservable({ fetchImageSuspended(page) })
} }
open fun fetchImageFlow(page: Page): Flow<Response> { open suspend fun fetchImageSuspended(page: Page): Response {
return flow { return withContext(Dispatchers.IO) {
emit( client.newCallWithProgress(imageRequestSuspended(page), page).await()
client.newCallWithProgress(imageRequestSuspended(page), page).await()
)
} }
} }

View File

@ -13,7 +13,7 @@ interface UrlImportableSource : Source {
} }
// This method is allowed to block for IO if necessary // This method is allowed to block for IO if necessary
fun mapUrlToMangaUrl(uri: Uri): String? suspend fun mapUrlToMangaUrl(uri: Uri): String?
fun cleanMangaUrl(url: String): String { fun cleanMangaUrl(url: String): String {
return try { return try {

View File

@ -910,7 +910,7 @@ class EHentai(
"e-hentai.org" "e-hentai.org"
) )
override fun mapUrlToMangaUrl(uri: Uri): String? { override suspend fun mapUrlToMangaUrl(uri: Uri): String? {
return when (uri.pathSegments.firstOrNull()) { return when (uri.pathSegments.firstOrNull()) {
"g" -> { "g" -> {
// Is already gallery page, do nothing // Is already gallery page, do nothing

View File

@ -128,7 +128,7 @@ class Hitomi(delegate: HttpSource, val context: Context) :
"hitomi.la" "hitomi.la"
) )
override fun mapUrlToMangaUrl(uri: Uri): String? { override suspend fun mapUrlToMangaUrl(uri: Uri): String? {
val lcFirstPathSegment = uri.pathSegments.firstOrNull()?.toLowerCase() ?: return null val lcFirstPathSegment = uri.pathSegments.firstOrNull()?.toLowerCase() ?: return null
if (lcFirstPathSegment != "manga" && lcFirstPathSegment != "reader") { if (lcFirstPathSegment != "manga" && lcFirstPathSegment != "reader") {

View File

@ -48,7 +48,6 @@ import exh.ui.metadata.adapters.MangaDexDescriptionAdapter
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import exh.widget.preference.MangadexLoginDialog import exh.widget.preference.MangadexLoginDialog
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.CacheControl import okhttp3.CacheControl
@ -101,7 +100,7 @@ class MangaDex(delegate: HttpSource, val context: Context) :
} }
} }
override fun mapUrlToMangaUrl(uri: Uri): String? { override suspend fun mapUrlToMangaUrl(uri: Uri): String? {
val lcFirstPathSegment = uri.pathSegments.firstOrNull()?.toLowerCase() ?: return null val lcFirstPathSegment = uri.pathSegments.firstOrNull()?.toLowerCase() ?: return null
return if (lcFirstPathSegment == "title" || lcFirstPathSegment == "manga") { return if (lcFirstPathSegment == "title" || lcFirstPathSegment == "manga") {
@ -152,7 +151,7 @@ class MangaDex(delegate: HttpSource, val context: Context) :
ApiMangaParser(listOf(mdLang)).parseIntoMetadata(metadata, input, preferences.mangaDexForceLatestCovers().get()) ApiMangaParser(listOf(mdLang)).parseIntoMetadata(metadata, input, preferences.mangaDexForceLatestCovers().get())
} }
override fun fetchFollows(): Observable<MangasPage> { override suspend fun fetchFollows(): MangasPage {
return FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).fetchFollows() return FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).fetchFollows()
} }
@ -226,36 +225,36 @@ class MangaDex(delegate: HttpSource, val context: Context) :
} }
} }
override fun fetchAllFollows(forceHd: Boolean): Flow<List<Pair<SManga, MangaDexSearchMetadata>>> { override suspend fun fetchAllFollows(forceHd: Boolean): List<Pair<SManga, MangaDexSearchMetadata>> {
return flow { emit(FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).fetchAllFollows(forceHd)) } return withContext(Dispatchers.IO) { FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).fetchAllFollows(forceHd) }
} }
fun updateReadingProgress(track: Track): Flow<Boolean> { suspend fun updateReadingProgress(track: Track): Boolean {
return flow { FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).updateReadingProgress(track) } return withContext(Dispatchers.IO) { FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).updateReadingProgress(track) }
} }
fun updateRating(track: Track): Flow<Boolean> { suspend fun updateRating(track: Track): Boolean {
return flow { FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).updateRating(track) } return withContext(Dispatchers.IO) { FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).updateRating(track) }
} }
override fun fetchTrackingInfo(url: String): Flow<Track> { override suspend fun fetchTrackingInfo(url: String): Track {
return flow { return withContext(Dispatchers.IO) {
if (!isLogged()) { if (!isLogged()) {
throw Exception("Not Logged in") throw Exception("Not Logged in")
} }
emit(FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).fetchTrackingInfo(url)) FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).fetchTrackingInfo(url)
} }
} }
override fun updateFollowStatus(mangaID: String, followStatus: FollowStatus): Flow<Boolean> { override suspend fun updateFollowStatus(mangaID: String, followStatus: FollowStatus): Boolean {
return flow { emit(FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).updateFollowStatus(mangaID, followStatus)) } return withContext(Dispatchers.IO) { FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).updateFollowStatus(mangaID, followStatus) }
} }
override fun getFilterHeader(controller: Controller): MangaDexFabHeaderAdapter { override fun getFilterHeader(controller: Controller): MangaDexFabHeaderAdapter {
return MangaDexFabHeaderAdapter(controller, this) return MangaDexFabHeaderAdapter(controller, this)
} }
override fun fetchRandomMangaUrl(): Flow<String> { override suspend fun fetchRandomMangaUrl(): String {
return MangaHandler(client, headers, listOf(mdLang)).fetchRandomMangaId() return MangaHandler(client, headers, listOf(mdLang)).fetchRandomMangaId()
} }

View File

@ -14,7 +14,6 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.SuspendHttpSource import eu.kanade.tachiyomi.source.online.SuspendHttpSource
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.lang.asFlow
import eu.kanade.tachiyomi.util.lang.await import eu.kanade.tachiyomi.util.lang.await
import eu.kanade.tachiyomi.util.lang.awaitSingle import eu.kanade.tachiyomi.util.lang.awaitSingle
import eu.kanade.tachiyomi.util.lang.awaitSingleOrNull import eu.kanade.tachiyomi.util.lang.awaitSingleOrNull
@ -22,13 +21,6 @@ import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
import exh.MERGED_SOURCE_ID import exh.MERGED_SOURCE_ID
import exh.merged.sql.models.MergedMangaReference import exh.merged.sql.models.MergedMangaReference
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapMerge
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@ -57,15 +49,15 @@ class MergedSource : SuspendHttpSource() {
override suspend fun chapterListParseSuspended(response: Response) = throw UnsupportedOperationException() override suspend fun chapterListParseSuspended(response: Response) = throw UnsupportedOperationException()
override suspend fun pageListParseSuspended(response: Response) = throw UnsupportedOperationException() override suspend fun pageListParseSuspended(response: Response) = throw UnsupportedOperationException()
override suspend fun imageUrlParseSuspended(response: Response) = throw UnsupportedOperationException() override suspend fun imageUrlParseSuspended(response: Response) = throw UnsupportedOperationException()
override fun fetchChapterListFlow(manga: SManga) = throw UnsupportedOperationException() override suspend fun fetchChapterListSuspended(manga: SManga) = throw UnsupportedOperationException()
override fun fetchImageFlow(page: Page) = throw UnsupportedOperationException() override suspend fun fetchImageSuspended(page: Page) = throw UnsupportedOperationException()
override fun fetchImageUrlFlow(page: Page) = throw UnsupportedOperationException() override suspend fun fetchImageUrlSuspended(page: Page) = throw UnsupportedOperationException()
override fun fetchPageListFlow(chapter: SChapter) = throw UnsupportedOperationException() override suspend fun fetchPageListSuspended(chapter: SChapter) = throw UnsupportedOperationException()
override fun fetchLatestUpdatesFlow(page: Int) = throw UnsupportedOperationException() override suspend fun fetchLatestUpdatesSuspended(page: Int) = throw UnsupportedOperationException()
override fun fetchPopularMangaFlow(page: Int) = throw UnsupportedOperationException() override suspend fun fetchPopularMangaSuspended(page: Int) = throw UnsupportedOperationException()
override fun fetchMangaDetailsFlow(manga: SManga): Flow<SManga> { override suspend fun fetchMangaDetailsSuspended(manga: SManga): SManga {
return flow { return withContext(Dispatchers.IO) {
val mergedManga = db.getManga(manga.url, id).await() ?: throw Exception("merged manga not in db") val mergedManga = db.getManga(manga.url, id).await() ?: throw Exception("merged manga not in db")
val mangaReferences = mergedManga.id?.let { withContext(Dispatchers.IO) { db.getMergedMangaReferences(it).await() } } ?: throw Exception("merged manga id is null") val mangaReferences = mergedManga.id?.let { withContext(Dispatchers.IO) { db.getMergedMangaReferences(it).await() } } ?: throw Exception("merged manga id is null")
if (mangaReferences.isEmpty()) throw IllegalArgumentException("Manga references are empty, info unavailable, merge is likely corrupted") if (mangaReferences.isEmpty()) throw IllegalArgumentException("Manga references are empty, info unavailable, merge is likely corrupted")
@ -75,14 +67,12 @@ class MergedSource : SuspendHttpSource() {
} }
) throw IllegalArgumentException("Manga references contain only the merged reference, merge is likely corrupted") ) throw IllegalArgumentException("Manga references contain only the merged reference, merge is likely corrupted")
emit( SManga.create().apply {
SManga.create().apply { val mangaInfoReference = mangaReferences.firstOrNull { it.isInfoManga } ?: mangaReferences.firstOrNull { it.mangaId != it.mergeId }
val mangaInfoReference = mangaReferences.firstOrNull { it.isInfoManga } ?: mangaReferences.firstOrNull { it.mangaId != it.mergeId } val dbManga = mangaInfoReference?.let { withContext(Dispatchers.IO) { db.getManga(it.mangaUrl, it.mangaSourceId).await() } }
val dbManga = mangaInfoReference?.let { withContext(Dispatchers.IO) { db.getManga(it.mangaUrl, it.mangaSourceId).await() } } this.copyFrom(dbManga ?: mergedManga)
this.copyFrom(dbManga ?: mergedManga) url = manga.url
url = manga.url }
}
)
} }
} }
@ -131,39 +121,45 @@ class MergedSource : SuspendHttpSource() {
return chapterList.maxByOrNull { it.chapter_number }?.manga_id return chapterList.maxByOrNull { it.chapter_number }?.manga_id
} }
fun fetchChaptersForMergedManga(manga: Manga, downloadChapters: Boolean = true, editScanlators: Boolean = false, dedupe: Boolean = true): Flow<List<Chapter>> { suspend fun fetchChaptersForMergedManga(manga: Manga, downloadChapters: Boolean = true, editScanlators: Boolean = false, dedupe: Boolean = true): List<Chapter> {
return flow { return withContext(Dispatchers.IO) {
withContext(Dispatchers.IO) { fetchChaptersAndSync(manga, downloadChapters)
fetchChaptersAndSync(manga, downloadChapters).collect() getChaptersFromDB(manga, editScanlators, dedupe).awaitSingleOrNull() ?: emptyList()
}
emit(
getChaptersFromDB(manga, editScanlators, dedupe).awaitSingleOrNull() ?: emptyList<Chapter>()
)
} }
} }
suspend fun fetchChaptersAndSync(manga: Manga, downloadChapters: Boolean = true): Flow<Pair<List<Chapter>, List<Chapter>>> { suspend fun fetchChaptersAndSync(manga: Manga, downloadChapters: Boolean = true): Pair<List<Chapter>, List<Chapter>> {
val mangaReferences = db.getMergedMangaReferences(manga.id!!).await() val mangaReferences = db.getMergedMangaReferences(manga.id!!).await()
if (mangaReferences.isEmpty()) throw IllegalArgumentException("Manga references are empty, chapters unavailable, merge is likely corrupted") if (mangaReferences.isEmpty()) throw IllegalArgumentException("Manga references are empty, chapters unavailable, merge is likely corrupted")
val ifDownloadNewChapters = downloadChapters && manga.shouldDownloadNewChapters(db, preferences) val ifDownloadNewChapters = downloadChapters && manga.shouldDownloadNewChapters(db, preferences)
return mangaReferences.filter { it.mangaSourceId != MERGED_SOURCE_ID }.asFlow().map { return mangaReferences.filter { it.mangaSourceId != MERGED_SOURCE_ID }.map {
load(db, sourceManager, it) load(db, sourceManager, it)
}.buffer().flatMapMerge { loadedManga -> }.mapNotNull { loadedManga ->
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
if (loadedManga.manga != null && loadedManga.reference.getChapterUpdates) { if (loadedManga.manga != null && loadedManga.reference.getChapterUpdates) {
loadedManga.source.fetchChapterList(loadedManga.manga).asFlow() loadedManga.source.fetchChapterList(loadedManga.manga).awaitSingle()
.map { syncChaptersWithSource(db, it, loadedManga.manga, loadedManga.source) } .let { syncChaptersWithSource(db, it, loadedManga.manga, loadedManga.source) }
.onEach { .also {
if (ifDownloadNewChapters && loadedManga.reference.downloadChapters) { if (ifDownloadNewChapters && loadedManga.reference.downloadChapters) {
downloadManager.downloadChapters(loadedManga.manga, it.first) downloadManager.downloadChapters(loadedManga.manga, it.first)
} }
} }
} else { } else {
emptyFlow() null
} }
} }
}.buffer() }.let { pairs ->
val firsts = mutableListOf<Chapter>()
val seconds = mutableListOf<Chapter>()
pairs.forEach {
firsts.addAll(it.first)
seconds.addAll(it.second)
}
firsts to seconds
}
} }
suspend fun load(db: DatabaseHelper, sourceManager: SourceManager, reference: MergedMangaReference): LoadedMangaSource { suspend fun load(db: DatabaseHelper, sourceManager: SourceManager, reference: MergedMangaReference): LoadedMangaSource {

View File

@ -165,7 +165,7 @@ class NHentai(delegate: HttpSource, val context: Context) :
"nhentai.net" "nhentai.net"
) )
override fun mapUrlToMangaUrl(uri: Uri): String? { override suspend fun mapUrlToMangaUrl(uri: Uri): String? {
if (uri.pathSegments.firstOrNull()?.toLowerCase() != "g") { if (uri.pathSegments.firstOrNull()?.toLowerCase() != "g") {
return null return null
} }

View File

@ -129,7 +129,7 @@ class PervEden(delegate: HttpSource, val context: Context) :
} }
} }
override fun mapUrlToMangaUrl(uri: Uri): String? { override suspend fun mapUrlToMangaUrl(uri: Uri): String? {
val newUri = "http://www.perveden.com/".toUri().buildUpon() val newUri = "http://www.perveden.com/".toUri().buildUpon()
uri.pathSegments.take(3).forEach { uri.pathSegments.take(3).forEach {
newUri.appendPath(it) newUri.appendPath(it)

View File

@ -93,7 +93,7 @@ class EightMuses(delegate: HttpSource, val context: Context) :
"8muses.com" "8muses.com"
) )
override fun mapUrlToMangaUrl(uri: Uri): String? { override suspend fun mapUrlToMangaUrl(uri: Uri): String? {
var path = uri.pathSegments.drop(2) var path = uri.pathSegments.drop(2)
if (uri.pathSegments[1].toLowerCase() == "picture") { if (uri.pathSegments[1].toLowerCase() == "picture") {
path = path.dropLast(1) path = path.dropLast(1)

View File

@ -83,7 +83,7 @@ class HBrowse(delegate: HttpSource, val context: Context) :
"hbrowse.com" "hbrowse.com"
) )
override fun mapUrlToMangaUrl(uri: Uri): String? { override suspend fun mapUrlToMangaUrl(uri: Uri): String? {
return "/${uri.pathSegments.first()}/c00001/" return "/${uri.pathSegments.first()}/c00001/"
} }

View File

@ -105,7 +105,7 @@ class HentaiCafe(delegate: HttpSource, val context: Context) :
"hentai.cafe" "hentai.cafe"
) )
override fun mapUrlToMangaUrl(uri: Uri): String? { override suspend fun mapUrlToMangaUrl(uri: Uri): String? {
val lcFirstPathSegment = uri.pathSegments.firstOrNull()?.takeUnless { it.equals("manga", true) } ?: return null val lcFirstPathSegment = uri.pathSegments.firstOrNull()?.takeUnless { it.equals("manga", true) } ?: return null
return if (lcFirstPathSegment.equals("hc.fyi", true)) { return if (lcFirstPathSegment.equals("hc.fyi", true)) {

View File

@ -114,7 +114,7 @@ class Pururin(delegate: HttpSource, val context: Context) :
"www.pururin.io" "www.pururin.io"
) )
override fun mapUrlToMangaUrl(uri: Uri): String? { override suspend fun mapUrlToMangaUrl(uri: Uri): String? {
return "${PururinSearchMetadata.BASE_URL}/gallery/${uri.pathSegments[1]}/${uri.lastPathSegment}" return "${PururinSearchMetadata.BASE_URL}/gallery/${uri.pathSegments[1]}/${uri.lastPathSegment}"
} }

View File

@ -40,7 +40,7 @@ class Tsumino(delegate: HttpSource, val context: Context) :
super.fetchSearchManga(page, query, filters) super.fetchSearchManga(page, query, filters)
} }
override fun mapUrlToMangaUrl(uri: Uri): String? { override suspend fun mapUrlToMangaUrl(uri: Uri): String? {
val lcFirstPathSegment = uri.pathSegments.firstOrNull()?.toLowerCase(Locale.ROOT) ?: return null val lcFirstPathSegment = uri.pathSegments.firstOrNull()?.toLowerCase(Locale.ROOT) ?: return null
if (lcFirstPathSegment != "read" && lcFirstPathSegment != "book" && lcFirstPathSegment != "entry") { if (lcFirstPathSegment != "read" && lcFirstPathSegment != "book" && lcFirstPathSegment != "entry") {
return null return null

View File

@ -34,7 +34,6 @@ import exh.md.utils.FollowStatus
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
import exh.util.isLewd import exh.util.isLewd
import exh.util.nullIfBlank import exh.util.nullIfBlank
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import rx.Observable import rx.Observable
import rx.Subscription import rx.Subscription
@ -543,7 +542,7 @@ class LibraryPresenter(
launchIO { launchIO {
MdUtil.getEnabledMangaDex(preferences, sourceManager)?.let { mdex -> MdUtil.getEnabledMangaDex(preferences, sourceManager)?.let { mdex ->
mangaList.forEach { mangaList.forEach {
mdex.updateFollowStatus(MdUtil.getMangaId(it.url), FollowStatus.READING).collect() mdex.updateFollowStatus(MdUtil.getMangaId(it.url), FollowStatus.READING)
} }
} }
} }

View File

@ -29,10 +29,10 @@ import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
import eu.kanade.tachiyomi.util.chapter.ChapterSettingsHelper import eu.kanade.tachiyomi.util.chapter.ChapterSettingsHelper
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.isLocal import eu.kanade.tachiyomi.util.isLocal
import eu.kanade.tachiyomi.util.lang.asObservable
import eu.kanade.tachiyomi.util.lang.await import eu.kanade.tachiyomi.util.lang.await
import eu.kanade.tachiyomi.util.lang.isNullOrUnsubscribed import eu.kanade.tachiyomi.util.lang.isNullOrUnsubscribed
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.runAsObservable
import eu.kanade.tachiyomi.util.prepUpdateCover import eu.kanade.tachiyomi.util.prepUpdateCover
import eu.kanade.tachiyomi.util.removeCovers import eu.kanade.tachiyomi.util.removeCovers
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
@ -766,7 +766,7 @@ class MangaPresenter(
) )
// SY --> // SY -->
} else { } else {
Observable.defer { source.fetchChaptersForMergedManga(manga, manualFetch, true, dedupe).asObservable() } Observable.defer { runAsObservable({ source.fetchChaptersForMergedManga(manga, manualFetch, true, dedupe) }) }
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.doOnNext { .doOnNext {

View File

@ -45,12 +45,11 @@ import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.source.getMainSource import exh.source.getMainSource
import exh.util.defaultReaderType import exh.util.defaultReaderType
import exh.util.shouldDeleteChapters import exh.util.shouldDeleteChapters
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import rx.Completable
import rx.Observable import rx.Observable
import rx.Subscription import rx.Subscription
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers

View File

@ -80,7 +80,7 @@ class GalleryAdder {
} }
// Map URL to manga URL // Map URL to manga URL
val realUrl = try { val realMangaUrl = try {
source.mapUrlToMangaUrl(uri) source.mapUrlToMangaUrl(uri)
} catch (e: Exception) { } catch (e: Exception) {
logger.e(context.getString(R.string.gallery_adder_uri_map_to_manga_error), e) logger.e(context.getString(R.string.gallery_adder_uri_map_to_manga_error), e)
@ -88,18 +88,18 @@ class GalleryAdder {
} ?: return GalleryAddEvent.Fail.UnknownType(url, context) } ?: return GalleryAddEvent.Fail.UnknownType(url, context)
// Clean URL // Clean URL
val cleanedUrl = try { val cleanedMangaUrl = try {
source.cleanMangaUrl(realUrl) source.cleanMangaUrl(realMangaUrl)
} catch (e: Exception) { } catch (e: Exception) {
logger.e(context.getString(R.string.gallery_adder_uri_clean_error), e) logger.e(context.getString(R.string.gallery_adder_uri_clean_error), e)
null null
} ?: return GalleryAddEvent.Fail.UnknownType(url, context) } ?: return GalleryAddEvent.Fail.UnknownType(url, context)
// Use manga in DB if possible, otherwise, make a new manga // Use manga in DB if possible, otherwise, make a new manga
val manga = db.getManga(cleanedUrl, source.id).executeOnIO() val manga = db.getManga(cleanedMangaUrl, source.id).executeOnIO()
?: Manga.create(source.id).apply { ?: Manga.create(source.id).apply {
this.url = cleanedUrl this.url = cleanedMangaUrl
title = realUrl title = realMangaUrl
} }
// Insert created manga if not in DB before fetching details // Insert created manga if not in DB before fetching details
@ -163,7 +163,8 @@ sealed class GalleryAddEvent {
class Success( class Success(
override val galleryUrl: String, override val galleryUrl: String,
val manga: Manga, val manga: Manga,
val context: Context val context: Context,
val chapter: Chapter? = null
) : GalleryAddEvent() { ) : GalleryAddEvent() {
override val galleryTitle = manga.title override val galleryTitle = manga.title
override val logMessage = context.getString(R.string.batch_add_success_log_message, galleryTitle) override val logMessage = context.getString(R.string.batch_add_success_log_message, galleryTitle)

View File

@ -16,7 +16,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.singleOrNull
import reactivecircus.flowbinding.android.view.clicks import reactivecircus.flowbinding.android.view.clicks
class MangaDexFabHeaderAdapter(val controller: Controller, val source: CatalogueSource) : class MangaDexFabHeaderAdapter(val controller: Controller, val source: CatalogueSource) :
@ -46,7 +45,7 @@ class MangaDexFabHeaderAdapter(val controller: Controller, val source: Catalogue
.launchIn(scope) .launchIn(scope)
binding.mangadexRandom.clicks() binding.mangadexRandom.clicks()
.onEach { .onEach {
(source as? RandomMangaSource)?.fetchRandomMangaUrl()?.singleOrNull()?.let { randomMangaId -> (source as? RandomMangaSource)?.fetchRandomMangaUrl()?.let { randomMangaId ->
controller.router.replaceTopController(BrowseSourceController(source, randomMangaId).withFadeTransaction()) controller.router.replaceTopController(BrowseSourceController(source, randomMangaId).withFadeTransaction())
} }
} }

View File

@ -3,6 +3,7 @@ package exh.md.follows
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.online.all.MangaDex import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.ui.browse.source.browse.Pager import eu.kanade.tachiyomi.ui.browse.source.browse.Pager
import eu.kanade.tachiyomi.util.lang.runAsObservable
import rx.Observable import rx.Observable
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
@ -13,7 +14,7 @@ import rx.schedulers.Schedulers
class MangaDexFollowsPager(val source: MangaDex) : Pager() { class MangaDexFollowsPager(val source: MangaDex) : Pager() {
override fun requestNext(): Observable<MangasPage> { override fun requestNext(): Observable<MangasPage> {
return source.fetchFollows() return runAsObservable({ source.fetchFollows() })
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.doOnNext { onPageReceived(it) } .doOnNext { onPageReceived(it) }

View File

@ -6,7 +6,6 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservable
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.MetadataMangasPage import eu.kanade.tachiyomi.source.model.MetadataMangasPage
@ -26,17 +25,16 @@ import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import rx.Observable
class FollowsHandler(val client: OkHttpClient, val headers: Headers, val preferences: PreferencesHelper, private val useLowQualityCovers: Boolean) { class FollowsHandler(val client: OkHttpClient, val headers: Headers, val preferences: PreferencesHelper, private val useLowQualityCovers: Boolean) {
/** /**
* fetch follows by page * fetch follows by page
*/ */
fun fetchFollows(): Observable<MangasPage> { suspend fun fetchFollows(): MangasPage {
return client.newCall(followsListRequest()) return client.newCall(followsListRequest())
.asObservable() .await()
.map { response -> .let { response ->
followsParseMangaPage(response) followsParseMangaPage(response)
} }
} }

View File

@ -8,8 +8,6 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.CacheControl import okhttp3.CacheControl
import okhttp3.Headers import okhttp3.Headers
@ -95,10 +93,10 @@ class MangaHandler(val client: OkHttpClient, val headers: Headers, val langs: Li
} }
} }
fun fetchRandomMangaId(): Flow<String> { suspend fun fetchRandomMangaId(): String {
return flow { return withContext(Dispatchers.IO) {
val response = client.newCall(randomMangaRequest()).await() val response = client.newCall(randomMangaRequest()).await()
emit(ApiMangaParser(langs).randomMangaIdParse(response)) ApiMangaParser(langs).randomMangaIdParse(response)
} }
} }