diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 75ac8b5a3..078873d5b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -130,6 +130,14 @@
android:host="g.e-hentai.org"
android:pathPrefix="/g/"
android:scheme="https"/>
+
+
()
init {
createSources()
+
+ //Rebuild sources when settings change
+ val action: Action1 = Action1 {
+ sourcesMap.clear()
+ createSources()
+ }
+ prefs.enableExhentai().asObservable().subscribe(action)
+ prefs.imageQuality().asObservable().subscribe (action)
+ prefs.useHentaiAtHome().asObservable().subscribe(action)
+ prefs.useJapaneseTitle().asObservable().subscribe {
+ action.call(null)
+ }
+ prefs.ehSearchSize().asObservable().subscribe (action)
+ prefs.thumbnailRows().asObservable().subscribe(action)
}
open fun get(sourceKey: Long): Source? {
@@ -39,6 +65,8 @@ open class SourceManager(private val context: Context) {
createExtensionSources().forEach { registerSource(it) }
createYamlSources().forEach { registerSource(it) }
createInternalSources().forEach { registerSource(it) }
+ //EH
+ createEHSources().forEach { registerSource(it) }
}
private fun registerSource(source: Source, overwrite: Boolean = false) {
@@ -61,6 +89,19 @@ open class SourceManager(private val context: Context) {
WieManga()
)
+ private fun createEHSources(): List {
+ //TODO Fix and hook up to createSources...
+ val exSrcs = mutableListOf(
+ EHentai(EH_SOURCE_ID, false, context),
+ EHentaiMetadata(EH_METADATA_SOURCE_ID, false, context)
+ )
+ if(prefs.enableExhentai().getOrDefault()) {
+ exSrcs += EHentai(EXH_SOURCE_ID, true, context)
+ exSrcs += EHentaiMetadata(EXH_METADATA_SOURCE_ID, true, context)
+ }
+ return exSrcs
+ }
+
private fun createYamlSources(): List {
val sources = mutableListOf()
diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt
index 0cf7c336d..2750e1f0c 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt
@@ -2,20 +2,17 @@ package eu.kanade.tachiyomi.source.online.all
import android.content.Context
import android.net.Uri
-import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
-import eu.kanade.tachiyomi.data.network.GET
-import eu.kanade.tachiyomi.data.network.asObservableSuccess
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
-import eu.kanade.tachiyomi.data.source.model.MangasPage
-import eu.kanade.tachiyomi.data.source.model.Page
-import eu.kanade.tachiyomi.data.source.online.OnlineSource
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.network.asObservableSuccess
+import eu.kanade.tachiyomi.source.model.*
+import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.asJsoup
import exh.metadata.*
import exh.metadata.models.ExGalleryMetadata
import exh.metadata.models.Tag
-import exh.plusAssign
import okhttp3.Response
import org.jsoup.nodes.Element
import rx.Observable
@@ -23,10 +20,11 @@ import uy.kohesive.injekt.injectLazy
import java.net.URLEncoder
import java.util.*
import exh.ui.login.LoginActivity
+import okhttp3.Request
-class EHentai(override val id: Int,
+class EHentai(override val id: Long,
val exh: Boolean,
- val context: Context) : OnlineSource() {
+ val context: Context) : HttpSource() {
val schema: String
get() = if(prefs.secureEXH().getOrDefault())
@@ -55,7 +53,7 @@ class EHentai(override val id: Int,
/**
* Parse a list of galleries
*/
- fun genericMangaParse(response: Response, page: MangasPage? = null)
+ fun genericMangaParse(response: Response)
= with(response.asJsoup()) {
//Parse mangas
val parsedMangas = select(".gtr0,.gtr1").map {
@@ -81,32 +79,27 @@ class EHentai(override val id: Int,
}
//Add to page if required
- page?.let { page ->
- page.mangas += parsedMangas.map { it.manga }
- select("a[onclick=return false]").last()?.let {
- if(it.text() == ">") page.nextPageUrl = it.attr("href")
- }
- }
- //Return parsed mangas anyways
- parsedMangas
+ val hasNextPage = select("a[onclick=return false]").last()?.let {
+ it.text() == ">"
+ } ?: false
+ MangasPage(parsedMangas.map { it.manga }, hasNextPage)
}
- override fun fetchChapterList(manga: Manga): Observable>
- = Observable.just(listOf(Chapter.create().apply {
- manga_id = manga.id
+ override fun fetchChapterList(manga: SManga): Observable>
+ = Observable.just(listOf(SChapter.create().apply {
url = manga.url
name = "Chapter"
chapter_number = 1f
}))
- override fun fetchPageListFromNetwork(chapter: Chapter)
- = fetchChapterPage(chapter, "$baseUrl${chapter.url}").map {
+ override fun fetchPageList(chapter: SChapter)
+ = fetchChapterPage(chapter, "$baseUrl/${chapter.url}").map {
it.mapIndexed { i, s ->
Page(i, s)
}
}!!
- private fun fetchChapterPage(chapter: Chapter, np: String,
+ private fun fetchChapterPage(chapter: SChapter, np: String,
pastUrls: List = emptyList()): Observable> {
val urls = ArrayList(pastUrls)
return chapterPageCall(np).flatMap {
@@ -134,60 +127,46 @@ class EHentai(override val id: Int,
return if (it.text() == ">") it.attr("href") else null
}
- private fun buildGenreString(filters: List): String {
- val genreString = StringBuilder()
- for (genre in GENRE_LIST) {
- genreString += "&f_"
- genreString += genre
- genreString += "="
- genreString += if (filters.isEmpty()
- || !filters
- .map { it.id }
- .find { it == genre }
- .isNullOrEmpty())
- "1"
- else
- "0"
- }
- return genreString.toString()
- }
-
- override fun popularMangaInitialUrl() = if(exh)
- latestUpdatesInitialUrl()
+ override fun popularMangaRequest(page: Int) = if(exh)
+ latestUpdatesRequest(page)
else
- "$baseUrl/toplist.php?tl=15"
+ exGet("$baseUrl/toplist.php?tl=15", page)
- override fun popularMangaParse(response: Response, page: MangasPage) {
- genericMangaParse(response, page)
+ override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
+ val uri = Uri.parse("$baseUrl$QUERY_PREFIX").buildUpon()
+ uri.appendQueryParameter("f_search", query)
+ filters.forEach {
+ if(it is UriFilter) it.addToUri(uri)
+ }
+ return exGet(uri.toString(), page)
}
- override fun searchMangaInitialUrl(query: String, filters: List)
- = "$baseUrl$QUERY_PREFIX${buildGenreString(filters)}&f_search=${URLEncoder.encode(query, "UTF-8")}"
+ override fun latestUpdatesRequest(page: Int) = exGet(baseUrl, page)
- override fun searchMangaParse(response: Response, page: MangasPage, query: String, filters: List) {
- genericMangaParse(response, page)
- }
+ override fun popularMangaParse(response: Response) = genericMangaParse(response)
+ override fun searchMangaParse(response: Response) = genericMangaParse(response)
+ override fun latestUpdatesParse(response: Response) = genericMangaParse(response)
- override fun latestUpdatesInitialUrl() = baseUrl
-
- override fun latestUpdatesParse(response: Response, page: MangasPage) {
- genericMangaParse(response, page)
- }
+ fun exGet(url: String, page: Int? = null)
+ = GET(page?.let {
+ addParam(url, "page", Integer.toString(page - 1))
+ } ?: url, headers)
/**
* Parse gallery page to metadata model
*/
- override fun mangaDetailsParse(response: Response, manga: Manga) = with(response.asJsoup()) {
+ override fun mangaDetailsParse(response: Response) = with(response.asJsoup()) {
val metdata = ExGalleryMetadata()
with(metdata) {
- url = manga.url
+ val manga = SManga.create()
+ url = response.request().url().toString()
exh = this@EHentai.exh
title = select("#gn").text().nullIfBlank()?.trim()
altTitle = select("#gj").text().nullIfBlank()?.trim()
thumbnailUrl = select("#gd1 img").attr("src").nullIfBlank()?.trim()
- genre = select(".ic").attr("alt").nullIfBlank()?.trim()
+ genre = select(".ic").parents().attr("href").nullIfBlank()?.trim()?.substringAfterLast('/')
uploader = select("#gdn").text().nullIfBlank()?.trim()
@@ -252,41 +231,25 @@ class EHentai(override val id: Int,
//Copy metadata to manga
copyTo(manga)
+
+ manga
}
}
- override fun chapterListParse(response: Response, chapters: MutableList) {
- throw UnsupportedOperationException()
- }
+ override fun chapterListParse(response: Response)
+ = throw UnsupportedOperationException("Unused method was called somehow!")
- override fun pageListParse(response: Response, pages: MutableList) {
- throw UnsupportedOperationException()
- }
+ override fun pageListParse(response: Response)
+ = throw UnsupportedOperationException("Unused method was called somehow!")
override fun imageUrlParse(response: Response): String {
- throw UnsupportedOperationException()
- }
-
- //Copy and paste from OnlineSource as we need the page argument
- override public fun fetchImageUrl(page: Page): Observable {
- page.status = Page.LOAD_PAGE
- return client
- .newCall(imageUrlRequest(page))
- .asObservableSuccess()
- .map { imageUrlParse(it, page) }
- .doOnError { page.status = Page.ERROR }
- .onErrorReturn { null }
- .doOnNext { page.imageUrl = it }
- .map { page }
- }
-
- fun imageUrlParse(response: Response, page: Page): String {
with(response.asJsoup()) {
val currentImage = select("img[onerror]").attr("src")
+ //TODO This doesn't work currently. Find a better way to do this
//Each press of the retry button will choose another server
- select("#loadfail").attr("onclick").nullIfBlank()?.let {
- page.url = addParam(page.url, "nl", it.substring(it.indexOf('\'') + 1 .. it.lastIndexOf('\'') - 1))
- }
+// select("#loadfail").attr("onclick").nullIfBlank()?.let {
+// page.url = addParam(page.url, "nl", it.substring(it.indexOf('\'') + 1 .. it.lastIndexOf('\'') - 1))
+// }
return currentImage
}
}
@@ -366,8 +329,70 @@ class EHentai(override val id: Int,
}.build()!!
//Filters
- val generatedFilters = GENRE_LIST.map { Filter(it, it) }
- override fun getFilterList() = generatedFilters
+ override fun getFilterList() = FilterList(
+ GenreGroup(),
+ AdvancedGroup()
+ )
+ private interface UriFilter {
+ fun addToUri(builder: Uri.Builder)
+ }
+
+ class GenreOption(name: String, val genreId: String): Filter.CheckBox(name, false), UriFilter {
+ override fun addToUri(builder: Uri.Builder) {
+ builder.appendQueryParameter("f_" + genreId, if(state) "1" else "0")
+ }
+ }
+ class GenreGroup : UriGroup("Genres", listOf(
+ GenreOption("DÅjinshi", "doujinshi"),
+ GenreOption("Manga", "manga"),
+ GenreOption("Artist CG", "artistcg"),
+ GenreOption("Game CG", "gamecg"),
+ GenreOption("Western", "western"),
+ GenreOption("Non-H", "non-h"),
+ GenreOption("Image Set", "imageset"),
+ GenreOption("Cosplay", "cosplay"),
+ GenreOption("Asian Porn", "asianporn"),
+ GenreOption("Misc", "misc")
+ ))
+
+ class AdvancedOption(name: String, val param: String, defValue: Boolean = false): Filter.CheckBox(name, defValue), UriFilter {
+ override fun addToUri(builder: Uri.Builder) {
+ if(state)
+ builder.appendQueryParameter(param, "on")
+ }
+ }
+ class RatingOption : Filter.Select("Minimum Rating", arrayOf(
+ "Any",
+ "2 stars",
+ "3 stars",
+ "4 stars",
+ "5 stars"
+ )), UriFilter {
+ override fun addToUri(builder: Uri.Builder) {
+ if(state > 0) builder.appendQueryParameter("f_srdd", Integer.toString(state + 1))
+ }
+ }
+
+ //Explicit type arg for listOf() to workaround this: KT-16570
+ class AdvancedGroup : UriGroup>("Advanced Options", listOf>(
+ AdvancedOption("Search Gallery Name", "f_sname", true),
+ AdvancedOption("Search Gallery Tags", "f_stags", true),
+ AdvancedOption("Search Gallery Description", "f_sdesc"),
+ AdvancedOption("Search Torrent Filenames", "f_storr"),
+ AdvancedOption("Only Show Galleries With Torrents", "f_sto"),
+ AdvancedOption("Search Low-Power Tags", "f_sdt1"),
+ AdvancedOption("Search Downvoted Tags", "f_sdt2"),
+ AdvancedOption("Show Expunged Galleries", "f_sh"),
+ RatingOption()
+ ))
+
+ open class UriGroup(name: String, state: List) : Filter.Group(name, state), UriFilter {
+ override fun addToUri(builder: Uri.Builder) {
+ state.forEach {
+ if(it is UriFilter) it.addToUri(builder)
+ }
+ }
+ }
override val name = if(exh)
"ExHentai"
@@ -376,7 +401,6 @@ class EHentai(override val id: Int,
companion object {
val QUERY_PREFIX = "?f_apply=Apply+Filter"
- val GENRE_LIST = arrayOf("doujinshi", "manga", "artistcg", "gamecg", "western", "non-h", "imageset", "cosplay", "asianporn", "misc")
val TR_SUFFIX = "TR"
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentaiMetadata.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentaiMetadata.kt
index 158b9fe1f..628c937e6 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentaiMetadata.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentaiMetadata.kt
@@ -3,9 +3,8 @@ package eu.kanade.tachiyomi.source.online.all
import android.content.Context
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
-import eu.kanade.tachiyomi.data.source.model.MangasPage
-import eu.kanade.tachiyomi.data.source.model.Page
-import eu.kanade.tachiyomi.data.source.online.OnlineSource
+import eu.kanade.tachiyomi.source.model.*
+import eu.kanade.tachiyomi.source.online.HttpSource
import exh.metadata.MetadataHelper
import exh.metadata.copyTo
import exh.metadata.models.ExGalleryMetadata
@@ -15,11 +14,35 @@ import rx.Observable
/**
* Offline metadata store source
+ *
+ * TODO This no longer fakes an online source because of technical reasons.
+ * If we still want offline search, we must find out a way to rearchitecture the source system so it supports
+ * online source faking again.
*/
-class EHentaiMetadata(override val id: Int,
+class EHentaiMetadata(override val id: Long,
val exh: Boolean,
- val context: Context) : OnlineSource() {
+ val context: Context) : HttpSource() {
+ override fun popularMangaRequest(page: Int)
+ = throw UnsupportedOperationException("Unused method called!")
+ override fun popularMangaParse(response: Response)
+ = throw UnsupportedOperationException("Unused method called!")
+ override fun searchMangaRequest(page: Int, query: String, filters: FilterList)
+ = throw UnsupportedOperationException("Unused method called!")
+ override fun searchMangaParse(response: Response)
+ = throw UnsupportedOperationException("Unused method called!")
+ override fun latestUpdatesRequest(page: Int)
+ = throw UnsupportedOperationException("Unused method called!")
+ override fun latestUpdatesParse(response: Response)
+ = throw UnsupportedOperationException("Unused method called!")
+ override fun mangaDetailsParse(response: Response)
+ = throw UnsupportedOperationException("Unused method called!")
+ override fun chapterListParse(response: Response)
+ = throw UnsupportedOperationException("Unused method called!")
+ override fun pageListParse(response: Response)
+ = throw UnsupportedOperationException("Unused method called!")
+ override fun imageUrlParse(response: Response)
+ = throw UnsupportedOperationException("Unused method called!")
val metadataHelper = MetadataHelper()
@@ -34,55 +57,14 @@ class EHentaiMetadata(override val id: Int,
override val supportsLatest: Boolean
get() = true
- override fun popularMangaInitialUrl(): String {
- throw UnsupportedOperationException()
- }
-
- override fun popularMangaParse(response: Response, page: MangasPage) {
- throw UnsupportedOperationException()
- }
-
- override fun searchMangaInitialUrl(query: String, filters: List): String {
- throw UnsupportedOperationException()
- }
-
- override fun searchMangaParse(response: Response, page: MangasPage, query: String, filters: List) {
- throw UnsupportedOperationException()
- }
-
- override fun latestUpdatesInitialUrl(): String {
- throw UnsupportedOperationException()
- }
-
- override fun latestUpdatesParse(response: Response, page: MangasPage) {
- throw UnsupportedOperationException()
- }
-
- override fun mangaDetailsParse(response: Response, manga: Manga) {
- throw UnsupportedOperationException()
- }
-
- override fun chapterListParse(response: Response, chapters: MutableList) {
- throw UnsupportedOperationException()
- }
-
- override fun pageListParse(response: Response, pages: MutableList) {
- throw UnsupportedOperationException()
- }
-
- override fun imageUrlParse(response: Response): String {
- throw UnsupportedOperationException()
- }
-
- override fun fetchChapterList(manga: Manga): Observable>
+ override fun fetchChapterList(manga: SManga): Observable>
= Observable.just(listOf(Chapter.create().apply {
- manga_id = manga.id
url = manga.url
name = "ONLINE - Chapter"
chapter_number = 1f
}))
- override fun fetchPageListFromNetwork(chapter: Chapter) = internalEx.fetchPageListFromNetwork(chapter)
+ override fun fetchPageList(chapter: SChapter) = internalEx.fetchPageList(chapter)
override fun fetchImageUrl(page: Page) = internalEx.fetchImageUrl(page)
@@ -98,38 +80,42 @@ class EHentaiMetadata(override val id: Int,
it.datePosted ?: 0
}
- override fun fetchPopularManga(page: MangasPage)
+ override fun fetchPopularManga(page: Int)
= Observable.fromCallable {
- page.mangas.addAll(metadataHelper.getAllGalleries().sortedByDescending {
+ MangasPage(metadataHelper.getAllGalleries().sortedByDescending {
it.ratingCount ?: 0
- }.mapToManga())
- page
+ }.mapToManga(), false)
}!!
- override fun fetchSearchManga(page: MangasPage, query: String, filters: List)
+ override fun fetchSearchManga(page: Int, query: String, filters: FilterList)
= Observable.fromCallable {
+ val genreGroup = filters.find {
+ it is EHentai.GenreGroup
+ }!! as EHentai.GenreGroup
+ val disableGenreFilter = genreGroup.state.find(EHentai.GenreOption::state) == null
+
val parsed = searchEngine.parseQuery(query)
- page.mangas.addAll(sortedByTimeGalleries().filter { manga ->
- filters.isEmpty() || filters.filter { it.id == manga.genre }.isNotEmpty()
+ MangasPage(sortedByTimeGalleries().filter { manga ->
+ disableGenreFilter || genreGroup.state.find {
+ it.state && it.genreId == manga.genre
+ } != null
}.filter {
searchEngine.matches(it, parsed)
- }.mapToManga())
- page
+ }.mapToManga(), false)
}!!
- override fun fetchLatestUpdates(page: MangasPage)
+ override fun fetchLatestUpdates(page: Int)
= Observable.fromCallable {
- page.mangas.addAll(sortedByTimeGalleries().mapToManga())
- page
+ MangasPage(sortedByTimeGalleries().mapToManga(), false)
}!!
- override fun fetchMangaDetails(manga: Manga) = Observable.fromCallable {
+ override fun fetchMangaDetails(manga: SManga) = Observable.fromCallable {
//Hack to convert the gallery into an online gallery when favoriting it or reading it
metadataHelper.fetchMetadata(manga.url, exh)?.copyTo(manga)
manga
}!!
- override fun getFilterList() = internalEx.getFilterList()
+ override fun getFilterList() = FilterList(EHentai.GenreGroup())
override val name: String
get() = if(exh) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt
index ef56d7029..c52a5b829 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt
@@ -9,17 +9,17 @@ import android.widget.FrameLayout
import eu.davidea.flexibleadapter4.FlexibleAdapter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
-import eu.kanade.tachiyomi.data.source.SourceManager
-import eu.kanade.tachiyomi.data.source.online.all.EHentai
-import eu.kanade.tachiyomi.data.source.online.all.EHentaiMetadata
+import eu.kanade.tachiyomi.source.SourceManager
+import eu.kanade.tachiyomi.source.online.all.EHentai
+import eu.kanade.tachiyomi.source.online.all.EHentaiMetadata
import eu.kanade.tachiyomi.util.inflate
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
+import exh.isLewdSource
import exh.metadata.MetadataHelper
import exh.search.SearchEngine
import kotlinx.android.synthetic.main.item_catalogue_grid.view.*
import uy.kohesive.injekt.injectLazy
import java.util.*
-import kotlin.concurrent.thread
/**
* Adapter storing a list of manga in a certain category.
@@ -95,7 +95,7 @@ class LibraryCategoryAdapter(val fragment: LibraryCategoryView) :
* @return true if the manga should be included, false otherwise.
*/
override fun filterObject(manga: Manga, query: String): Boolean = with(manga) {
- if(manga.source > 100) {
+ if(!isLewdSource(manga.source)) {
//Regular searching for normal manga
title.toLowerCase().contains(query) ||
author != null && author!!.toLowerCase().contains(query)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
index ed504b977..4bda35e74 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
@@ -20,6 +20,7 @@ import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadFragment
import eu.kanade.tachiyomi.ui.setting.SettingsActivity
import exh.ui.batchadd.BatchAddFragment
import exh.ui.migration.LibraryMigrationManager
+import exh.ui.migration.SourceMigrator
import exh.ui.migration.UrlMigrator
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.toolbar.*
@@ -89,25 +90,31 @@ class MainActivity : BaseActivity() {
}
if (savedState == null) {
- // Set start screen
- setSelectedDrawerItem(startScreenId)
+ //Perform source migration
+ SourceMigrator().tryMigrationWithDialog(this, {
- // Show changelog if needed
- ChangelogDialogFragment.show(this, preferences, supportFragmentManager)
+ // Set start screen
+ try {
+ setSelectedDrawerItem(startScreenId)
+ } catch(e: Exception) {}
- // Migrate library if needed
- LibraryMigrationManager(this, dismissQueue).askMigrationIfNecessary()
+ // Show changelog if needed
+ ChangelogDialogFragment.show(this, preferences, supportFragmentManager)
- //Last part of migration requires finishing this activity
- finishSubscription?.unsubscribe()
- preferences.finishMainActivity().set(false)
- finishSubscription = preferences.finishMainActivity().asObservable().subscribe {
- if(it)
- finish()
- }
+ // Migrate library if needed
+ LibraryMigrationManager(this, dismissQueue).askMigrationIfNecessary()
- //Migrate URLs if necessary
- UrlMigrator().tryMigration()
+ //Last part of migration requires finishing this activity
+ finishSubscription?.unsubscribe()
+ preferences.finishMainActivity().set(false)
+ finishSubscription = preferences.finishMainActivity().asObservable().subscribe {
+ if (it)
+ finish()
+ }
+
+ //Migrate URLs if necessary
+ UrlMigrator().tryMigration()
+ })
}
}
diff --git a/app/src/main/java/exh/EHSourceHelpers.kt b/app/src/main/java/exh/EHSourceHelpers.kt
index 7f6e120b3..4b9f1f3fb 100644
--- a/app/src/main/java/exh/EHSourceHelpers.kt
+++ b/app/src/main/java/exh/EHSourceHelpers.kt
@@ -1,5 +1,17 @@
package exh
/**
- * Created by nulldev on 2/28/17.
+ * Source helpers
*/
+
+val LEWD_SOURCE_SERIES = 6900L
+val EH_SOURCE_ID = LEWD_SOURCE_SERIES + 1
+val EXH_SOURCE_ID = LEWD_SOURCE_SERIES + 2
+val EH_METADATA_SOURCE_ID = LEWD_SOURCE_SERIES + 3
+val EXH_METADATA_SOURCE_ID = LEWD_SOURCE_SERIES + 4
+
+fun isLewdSource(source: Long) = source >= 6900
+ && source <= 6999
+
+fun isExSource(source: Long) = source == EXH_SOURCE_ID
+ || source == EXH_METADATA_SOURCE_ID
diff --git a/app/src/main/java/exh/GalleryAdder.kt b/app/src/main/java/exh/GalleryAdder.kt
index 5f5bb9cb9..2e428a50e 100644
--- a/app/src/main/java/exh/GalleryAdder.kt
+++ b/app/src/main/java/exh/GalleryAdder.kt
@@ -2,14 +2,15 @@ package exh
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga
-import eu.kanade.tachiyomi.data.source.SourceManager
-import eu.kanade.tachiyomi.util.UrlUtil
+import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.util.syncChaptersWithSource
import exh.metadata.MetadataHelper
import exh.metadata.copyTo
import timber.log.Timber
import uy.kohesive.injekt.injectLazy
import java.net.MalformedURLException
+import java.net.URI
+import java.net.URISyntaxException
import java.net.URL
class GalleryAdder {
@@ -22,19 +23,20 @@ class GalleryAdder {
fun addGallery(url: String, fav: Boolean = false): Manga {
val source = when(URL(url).host) {
- "g.e-hentai.org", "e-hentai.org" -> 1
- "exhentai.org" -> 2
+ "g.e-hentai.org", "e-hentai.org" -> EH_SOURCE_ID
+ "exhentai.org" -> EXH_SOURCE_ID
else -> throw MalformedURLException("Not a valid gallery URL!")
}
val sourceObj = sourceManager.get(source)
?: throw IllegalStateException("Could not find EH source!")
- val pathOnlyUrl = UrlUtil.getPath(url)
+ val pathOnlyUrl = getUrlWithoutDomain(url)
//Use manga in DB if possible, otherwise, make a new manga
val manga = db.getManga(pathOnlyUrl, source).executeAsBlocking()
- ?: Manga.create(pathOnlyUrl, source).apply {
+ ?: Manga.create(source).apply {
+ this.url = pathOnlyUrl
title = url
}
@@ -42,7 +44,7 @@ class GalleryAdder {
manga.copyFrom(sourceObj.fetchMangaDetails(manga).toBlocking().first())
//Apply metadata
- metadataHelper.fetchMetadata(url, source == 2)?.copyTo(manga)
+ metadataHelper.fetchMetadata(url, isExSource(source))?.copyTo(manga)
if(fav) manga.favorite = true
@@ -61,4 +63,18 @@ class GalleryAdder {
return manga
}
+
+ private fun getUrlWithoutDomain(orig: String): String {
+ try {
+ val uri = URI(orig)
+ var out = uri.path
+ if (uri.query != null)
+ out += "?" + uri.query
+ if (uri.fragment != null)
+ out += "#" + uri.fragment
+ return out
+ } catch (e: URISyntaxException) {
+ return orig
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/exh/metadata/MetdataCopier.kt b/app/src/main/java/exh/metadata/MetdataCopier.kt
index 8e55cae39..bc079d46b 100644
--- a/app/src/main/java/exh/metadata/MetdataCopier.kt
+++ b/app/src/main/java/exh/metadata/MetdataCopier.kt
@@ -1,9 +1,8 @@
package exh.metadata
-import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
-import eu.kanade.tachiyomi.util.UrlUtil
+import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.models.ExGalleryMetadata
import exh.metadata.models.Tag
import exh.plusAssign
@@ -28,16 +27,18 @@ val EX_DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US)
private val prefs: PreferencesHelper by injectLazy()
-fun ExGalleryMetadata.copyTo(manga: Manga) {
- exh?.let {
+fun ExGalleryMetadata.copyTo(manga: SManga) {
+ //TODO Find some way to do this with SManga
+ /*exh?.let {
manga.source = if(it)
2
else
1
- }
+ }*/
url?.let { manga.url = it }
thumbnailUrl?.let { manga.thumbnail_url = it }
+ //No title bug?
val titleObj = if(prefs.useJapaneseTitle().getOrDefault())
altTitle ?: title
else
@@ -57,12 +58,12 @@ fun ExGalleryMetadata.copyTo(manga: Manga) {
//Try to automatically identify if it is ongoing, we try not to be too lenient here to avoid making mistakes
//We default to completed
- manga.status = Manga.COMPLETED
+ manga.status = SManga.COMPLETED
title?.let { t ->
ONGOING_SUFFIX.find {
t.endsWith(it, ignoreCase = true)
}?.let {
- manga.status = Manga.ONGOING
+ manga.status = SManga.ONGOING
}
}
diff --git a/app/src/main/java/exh/ui/migration/MetadataFetchDialog.kt b/app/src/main/java/exh/ui/migration/MetadataFetchDialog.kt
index 922bd25be..8db9a3221 100644
--- a/app/src/main/java/exh/ui/migration/MetadataFetchDialog.kt
+++ b/app/src/main/java/exh/ui/migration/MetadataFetchDialog.kt
@@ -7,8 +7,9 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
-import eu.kanade.tachiyomi.data.source.SourceManager
-import eu.kanade.tachiyomi.data.source.online.all.EHentai
+import eu.kanade.tachiyomi.source.SourceManager
+import eu.kanade.tachiyomi.source.online.all.EHentai
+import exh.isExSource
import exh.metadata.MetadataHelper
import exh.metadata.copyTo
import timber.log.Timber
@@ -44,7 +45,7 @@ class MetadataFetchDialog {
.executeAsBlocking()
.filter {
it.source <= 2
- && !metadataHelper.hasMetadata(it.url, it.source == 2)
+ && !metadataHelper.hasMetadata(it.url, isExSource(it.source))
}
context.runOnUiThread {
@@ -91,7 +92,7 @@ class MetadataFetchDialog {
db.getLibraryMangas().asRxSingle().subscribe {
//Not logged in but have ExHentai galleries
if(!preferenceHelper.enableExhentai().getOrDefault()) {
- it.find { it.source == 2 }?.let {
+ it.find { isExSource(it.source) }?.let {
extra = "If you use ExHentai, please log in first before fetching your library metadata!
"
}
}
diff --git a/app/src/main/java/exh/ui/migration/MigrationCompletionActivity.kt b/app/src/main/java/exh/ui/migration/MigrationCompletionActivity.kt
index 70fe14cac..76d0971cf 100644
--- a/app/src/main/java/exh/ui/migration/MigrationCompletionActivity.kt
+++ b/app/src/main/java/exh/ui/migration/MigrationCompletionActivity.kt
@@ -56,6 +56,9 @@ class MigrationCompletionActivity : BaseActivity() {
//Migrate urls
UrlMigrator().perform()
+ //Migrate source IDs
+ SourceMigrator().perform()
+
//Go back to MainActivity
//Set final steps
preferenceManager.migrationStatus().set(MigrationStatus.FINALIZE_MIGRATION)
diff --git a/app/src/main/java/exh/ui/migration/SourceMigrator.kt b/app/src/main/java/exh/ui/migration/SourceMigrator.kt
index afb75b6fb..020026dfa 100644
--- a/app/src/main/java/exh/ui/migration/SourceMigrator.kt
+++ b/app/src/main/java/exh/ui/migration/SourceMigrator.kt
@@ -1,5 +1,74 @@
package exh.ui.migration
-/**
- * Created by nulldev on 2/28/17.
- */
+import android.app.Activity
+import com.afollestad.materialdialogs.MaterialDialog
+import eu.kanade.tachiyomi.data.backup.BackupManager
+import eu.kanade.tachiyomi.data.database.DatabaseHelper
+import eu.kanade.tachiyomi.data.preference.PreferencesHelper
+import eu.kanade.tachiyomi.data.preference.getOrDefault
+import exh.LEWD_SOURCE_SERIES
+import timber.log.Timber
+import uy.kohesive.injekt.injectLazy
+import java.io.File
+import kotlin.concurrent.thread
+
+class SourceMigrator {
+
+ val db: DatabaseHelper by injectLazy()
+
+ val prefs: PreferencesHelper by injectLazy()
+
+ val backupManager by lazy {
+ BackupManager(db)
+ }
+
+ fun perform() {
+ db.insertMangas(db.getMangas().executeAsBlocking().map {
+ if(it.source < 100) {
+ if(it.url.trim('/').startsWith("g/")) {
+ //EH source, move ID
+ it.source += LEWD_SOURCE_SERIES
+ }
+ } else if(it.source < 200) {
+ //Regular source, move ID down
+ it.source -= 100
+ }
+ it
+ }).executeAsBlocking()
+ }
+
+ fun tryMigrationWithDialog(context: Activity, callback: () -> Unit) {
+ if(!prefs.hasPerformedSourceMigration().getOrDefault()) {
+ val dialog = MaterialDialog.Builder(context)
+ .title("Migrating galleries")
+ .progress(true, 0)
+ .cancelable(false)
+ .show()
+
+ thread {
+ try {
+ context.runOnUiThread {
+ dialog.setContent("Backing up library...")
+ }
+ backupManager.backupToFile(File(context.filesDir, "teh-source-migration-bck.json"))
+ context.runOnUiThread {
+ dialog.setContent("Performing migration...")
+ }
+ perform()
+ context.runOnUiThread {
+ dialog.setContent("Completing migration...")
+ }
+ prefs.hasPerformedSourceMigration().set(true)
+ dialog.dismiss()
+ } catch(e: Exception) {
+ Timber.e(e, "Error migrating source IDs!")
+ }
+ context.runOnUiThread {
+ callback()
+ }
+ }
+ } else {
+ callback()
+ }
+ }
+}
diff --git a/app/src/main/java/exh/ui/migration/UrlMigrator.kt b/app/src/main/java/exh/ui/migration/UrlMigrator.kt
index 55ccf373a..8665ed84e 100644
--- a/app/src/main/java/exh/ui/migration/UrlMigrator.kt
+++ b/app/src/main/java/exh/ui/migration/UrlMigrator.kt
@@ -4,6 +4,8 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
+import exh.isExSource
+import exh.isLewdSource
import exh.metadata.MetadataHelper
import uy.kohesive.injekt.injectLazy
@@ -21,8 +23,7 @@ class UrlMigrator {
//Find all EX mangas
val qualifyingMangas = dbMangas.asSequence().filter {
- it.source > 0
- && it.source <= 4
+ isLewdSource(it.source)
}
val possibleDups = mutableListOf()
@@ -42,8 +43,7 @@ class UrlMigrator {
//Build fixed URL
val urlWithSlash = "/" + it.url
//Fix metadata if required
- val metadata = metadataHelper.fetchMetadata(it.url, it.source == 2
- || it.source == 4)
+ val metadata = metadataHelper.fetchMetadata(it.url, isExSource(it.source))
metadata?.url?.let {
if(it.startsWith("g/")) { //Check if metadata URL has no slash
metadata.url = urlWithSlash //Fix it