Add favorites downloader.
This commit is contained in:
parent
492adb7035
commit
d981c75600
@ -22,7 +22,10 @@ import java.util.*
|
||||
import exh.ui.login.LoginActivity
|
||||
import exh.util.UriFilter
|
||||
import exh.util.UriGroup
|
||||
import okhttp3.CacheControl
|
||||
import okhttp3.Headers
|
||||
import okhttp3.Request
|
||||
import org.jsoup.nodes.Document
|
||||
|
||||
class EHentai(override val id: Long,
|
||||
val exh: Boolean,
|
||||
@ -38,7 +41,7 @@ class EHentai(override val id: Long,
|
||||
get() = if(exh)
|
||||
"$schema://exhentai.org"
|
||||
else
|
||||
"http://e-hentai.org"
|
||||
"$schema://e-hentai.org"
|
||||
|
||||
override val lang = "all"
|
||||
override val supportsLatest = true
|
||||
@ -52,11 +55,8 @@ class EHentai(override val id: Long,
|
||||
*/
|
||||
data class ParsedManga(val fav: String?, val manga: Manga)
|
||||
|
||||
/**
|
||||
* Parse a list of galleries
|
||||
*/
|
||||
fun genericMangaParse(response: Response)
|
||||
= with(response.asJsoup()) {
|
||||
fun extendedGenericMangaParse(doc: Document)
|
||||
= with(doc) {
|
||||
//Parse mangas
|
||||
val parsedMangas = select(".gtr0,.gtr1").map {
|
||||
ParsedManga(
|
||||
@ -84,7 +84,15 @@ class EHentai(override val id: Long,
|
||||
val hasNextPage = select("a[onclick=return false]").last()?.let {
|
||||
it.text() == ">"
|
||||
} ?: false
|
||||
MangasPage(parsedMangas.map { it.manga }, hasNextPage)
|
||||
Pair(parsedMangas, hasNextPage)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a list of galleries
|
||||
*/
|
||||
fun genericMangaParse(response: Response)
|
||||
= extendedGenericMangaParse(response.asJsoup()).let {
|
||||
MangasPage(it.first.map { it.manga }, it.second)
|
||||
}
|
||||
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>>
|
||||
@ -143,16 +151,29 @@ class EHentai(override val id: Long,
|
||||
return exGet(uri.toString(), page)
|
||||
}
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) = exGet(baseUrl, page)
|
||||
override fun latestUpdatesRequest(page: Int) = exGet(baseUrl, page)!!
|
||||
|
||||
override fun popularMangaParse(response: Response) = genericMangaParse(response)
|
||||
override fun searchMangaParse(response: Response) = genericMangaParse(response)
|
||||
override fun latestUpdatesParse(response: Response) = genericMangaParse(response)
|
||||
|
||||
fun exGet(url: String, page: Int? = null)
|
||||
fun exGet(url: String, page: Int? = null, additionalHeaders: Headers? = null, cache: Boolean = true)
|
||||
= GET(page?.let {
|
||||
addParam(url, "page", Integer.toString(page - 1))
|
||||
} ?: url, headers)
|
||||
} ?: url, additionalHeaders?.let {
|
||||
val headers = headers.newBuilder()
|
||||
it.toMultimap().forEach { t, u ->
|
||||
u.forEach {
|
||||
headers.add(t, it)
|
||||
}
|
||||
}
|
||||
headers.build()
|
||||
} ?: headers).let {
|
||||
if(!cache)
|
||||
it.newBuilder().cacheControl(CacheControl.FORCE_NETWORK).build()
|
||||
else
|
||||
it
|
||||
}!!
|
||||
|
||||
/**
|
||||
* Parse gallery page to metadata model
|
||||
@ -266,6 +287,37 @@ class EHentai(override val id: Long,
|
||||
throw UnsupportedOperationException("Unused method was called somehow!")
|
||||
}
|
||||
|
||||
//Too lazy to write return type
|
||||
fun fetchFavorites() = {
|
||||
//Used to get "s" cookie
|
||||
val favoriteUrl = "$baseUrl/favorites.php"
|
||||
val result = mutableListOf<ParsedManga>()
|
||||
var page = 1
|
||||
|
||||
var favNames: List<String>? = null
|
||||
|
||||
do {
|
||||
val response2 = client.newCall(exGet(favoriteUrl,
|
||||
page = page,
|
||||
cache = false)).execute()
|
||||
val doc = response2.asJsoup()
|
||||
|
||||
//Parse favorites
|
||||
val parsed = extendedGenericMangaParse(doc)
|
||||
result += parsed.first
|
||||
|
||||
//Parse fav names
|
||||
if (favNames == null)
|
||||
favNames = doc.getElementsByClass("nosel").first().children().filter {
|
||||
it.children().size >= 3
|
||||
}.map { it.child(2).text() }.filterNotNull()
|
||||
|
||||
//Next page
|
||||
page++
|
||||
} while (parsed.second)
|
||||
Pair(result as List<ParsedManga>, favNames!!)
|
||||
}()
|
||||
|
||||
val cookiesHeader by lazy {
|
||||
val cookies: MutableMap<String, String> = mutableMapOf()
|
||||
if(prefs.enableExhentai().getOrDefault()) {
|
||||
@ -403,5 +455,20 @@ class EHentai(override val id: Long,
|
||||
companion object {
|
||||
val QUERY_PREFIX = "?f_apply=Apply+Filter"
|
||||
val TR_SUFFIX = "TR"
|
||||
|
||||
fun getCookies(cookies: String): Map<String, String>? {
|
||||
val foundCookies = HashMap<String, String>()
|
||||
for (cookie in cookies.split(";".toRegex()).dropLastWhile(String::isEmpty).toTypedArray()) {
|
||||
val splitCookie = cookie.split("=".toRegex()).dropLastWhile(String::isEmpty).toTypedArray()
|
||||
if (splitCookie.size < 2) {
|
||||
return null
|
||||
}
|
||||
val trimmedKey = splitCookie[0].trim { it <= ' ' }
|
||||
if (!foundCookies.containsKey(trimmedKey)) {
|
||||
foundCookies.put(trimmedKey, splitCookie[1].trim { it <= ' ' })
|
||||
}
|
||||
}
|
||||
return foundCookies
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.inflate
|
||||
import eu.kanade.tachiyomi.util.toast
|
||||
import eu.kanade.tachiyomi.widget.DialogCheckboxView
|
||||
import exh.FavoritesSyncHelper
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import kotlinx.android.synthetic.main.fragment_library.*
|
||||
import nucleus.factory.RequiresPresenter
|
||||
@ -267,6 +268,11 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
|
||||
val intent = CategoryActivity.newIntent(activity)
|
||||
startActivity(intent)
|
||||
}
|
||||
R.id.action_sync -> {
|
||||
FavoritesSyncHelper(this.activity).guiSyncFavorites({
|
||||
//Do we even need stuff in here?
|
||||
})
|
||||
}
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
|
135
app/src/main/java/exh/FavoritesSyncHelper.kt
Normal file
135
app/src/main/java/exh/FavoritesSyncHelper.kt
Normal file
@ -0,0 +1,135 @@
|
||||
package exh
|
||||
|
||||
import android.app.Activity
|
||||
import android.support.v7.app.AlertDialog
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||
import eu.kanade.tachiyomi.util.syncChaptersWithSource
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class FavoritesSyncHelper(val activity: Activity) {
|
||||
|
||||
val db: DatabaseHelper by injectLazy()
|
||||
|
||||
val sourceManager: SourceManager by injectLazy()
|
||||
|
||||
val prefs: PreferencesHelper by injectLazy()
|
||||
|
||||
fun guiSyncFavorites(onComplete: () -> Unit) {
|
||||
//ExHentai must be enabled/user must be logged in
|
||||
if (!prefs.enableExhentai().getOrDefault()) {
|
||||
AlertDialog.Builder(activity).setTitle("Error")
|
||||
.setMessage("You are not logged in! Please log in and try again!")
|
||||
.setPositiveButton("Ok") { dialog, _ -> dialog.dismiss() }.show()
|
||||
return
|
||||
}
|
||||
val dialog = MaterialDialog.Builder(activity)
|
||||
.progress(true, 0)
|
||||
.title("Downloading favorites")
|
||||
.content("Please wait...")
|
||||
.cancelable(false)
|
||||
.show()
|
||||
thread {
|
||||
var error = false
|
||||
try {
|
||||
syncFavorites()
|
||||
} catch (e: Exception) {
|
||||
error = true
|
||||
Timber.e(e, "Could not sync favorites!")
|
||||
}
|
||||
|
||||
dialog.dismiss()
|
||||
|
||||
activity.runOnUiThread {
|
||||
if (error)
|
||||
MaterialDialog.Builder(activity)
|
||||
.title("Error")
|
||||
.content("There was an error downloading your favorites, please try again later!")
|
||||
.positiveText("Ok")
|
||||
.show()
|
||||
onComplete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun syncFavorites() {
|
||||
val onlineSources = sourceManager.getOnlineSources()
|
||||
var ehSource: EHentai? = null
|
||||
var exSource: EHentai? = null
|
||||
onlineSources.forEach {
|
||||
if(it.id == EH_SOURCE_ID)
|
||||
ehSource = it as EHentai
|
||||
else if(it.id == EXH_SOURCE_ID)
|
||||
exSource = it as EHentai
|
||||
}
|
||||
|
||||
(exSource ?: ehSource)?.let { source ->
|
||||
val favResponse = source.fetchFavorites()
|
||||
val ourCategories = ArrayList<Category>(db.getCategories().executeAsBlocking())
|
||||
val ourMangas = ArrayList<Manga>(db.getMangas().executeAsBlocking())
|
||||
//Add required categories (categories do not sync upwards)
|
||||
favResponse.second.filter { theirCategory ->
|
||||
ourCategories.find {
|
||||
it.name.endsWith(theirCategory)
|
||||
} == null
|
||||
}.map {
|
||||
Category.create(it)
|
||||
}.let {
|
||||
db.inTransaction {
|
||||
//Insert new categories
|
||||
db.insertCategories(it).executeAsBlocking().results().entries.filter {
|
||||
it.value.wasInserted()
|
||||
}.forEach { it.key.id = it.value.insertedId()!!.toInt() }
|
||||
|
||||
val categoryMap = (it + ourCategories).associateBy { it.name }
|
||||
|
||||
//Insert new mangas
|
||||
val mangaToInsert = java.util.ArrayList<Manga>()
|
||||
favResponse.first.map {
|
||||
val category = categoryMap[it.fav]!!
|
||||
var manga = it.manga
|
||||
val alreadyHaveManga = ourMangas.find {
|
||||
it.url == manga.url
|
||||
}?.apply {
|
||||
manga = this
|
||||
} != null
|
||||
if (!alreadyHaveManga) {
|
||||
ourMangas.add(manga)
|
||||
mangaToInsert.add(manga)
|
||||
}
|
||||
manga.favorite = true
|
||||
Pair(manga, category)
|
||||
}.apply {
|
||||
//Insert mangas
|
||||
db.insertMangas(mangaToInsert).executeAsBlocking().results().entries.filter {
|
||||
it.value.wasInserted()
|
||||
}.forEach { manga ->
|
||||
manga.key.id = manga.value.insertedId()
|
||||
try {
|
||||
source.fetchChapterList(manga.key).map {
|
||||
syncChaptersWithSource(db, it, manga.key, source)
|
||||
}.toBlocking().first()
|
||||
} catch (e: Exception) {
|
||||
Timber.w(e, "Failed to update chapters for gallery: ${manga.key.title}!")
|
||||
}
|
||||
}
|
||||
|
||||
//Set categories
|
||||
val categories = map { MangaCategory.create(it.first, it.second) }
|
||||
val mangas = map { it.first }
|
||||
db.setMangaCategories(categories, mangas)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -20,11 +20,12 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper;
|
||||
import eu.kanade.tachiyomi.data.database.models.Category;
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaCategory;
|
||||
import eu.kanade.tachiyomi.source.online.all.EHentai;
|
||||
import kotlin.Pair;
|
||||
//import eu.kanade.tachiyomi.data.source.online.english.EHentai;
|
||||
|
||||
public class FavoritesSyncManager {
|
||||
/*
|
||||
Context context;
|
||||
/*Context context;
|
||||
DatabaseHelper db;
|
||||
|
||||
public FavoritesSyncManager(Context context, DatabaseHelper db) {
|
||||
@ -72,10 +73,10 @@ public class FavoritesSyncManager {
|
||||
mainLooper.post(onComplete);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
}*/
|
||||
/*
|
||||
public void syncFavorites() throws IOException {
|
||||
EHentai.FavoritesResponse favResponse = EHentai.fetchFavorites(context);
|
||||
Pair favResponse = EHentai.fetchFavorites(context);
|
||||
Map<String, List<Manga>> favorites = favResponse.favs;
|
||||
List<Category> ourCategories = new ArrayList<>(db.getCategories().executeAsBlocking());
|
||||
List<Manga> ourMangas = new ArrayList<>(db.getMangas().executeAsBlocking());
|
||||
@ -136,7 +137,7 @@ public class FavoritesSyncManager {
|
||||
for(Map.Entry<Manga, Category> entry : mangaToSetCategories.entrySet()) {
|
||||
db.setMangaCategories(Collections.singletonList(MangaCategory.Companion.create(entry.getKey(), entry.getValue())),
|
||||
Collections.singletonList(entry.getKey()));
|
||||
}
|
||||
}*/
|
||||
//Determines what
|
||||
/*Map<Integer, List<Manga>> toUpload = new HashMap<>();
|
||||
for (Manga manga : ourMangas) {
|
||||
|
@ -7,11 +7,18 @@ import android.view.MenuItem
|
||||
import android.webkit.CookieManager
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
||||
import exh.EXH_SOURCE_ID
|
||||
import kotlinx.android.synthetic.main.eh_activity_login.*
|
||||
import kotlinx.android.synthetic.main.toolbar.*
|
||||
import rx.Observable
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.net.HttpCookie
|
||||
@ -24,6 +31,8 @@ class LoginActivity : BaseActivity() {
|
||||
|
||||
val preferenceManager: PreferencesHelper by injectLazy()
|
||||
|
||||
val sourceManager: SourceManager by injectLazy()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setAppTheme()
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -73,13 +82,40 @@ class LoginActivity : BaseActivity() {
|
||||
//At ExHentai, check that everything worked out...
|
||||
if(applyExHentaiCookies(url)) {
|
||||
preferenceManager.enableExhentai().set(true)
|
||||
onBackPressed()
|
||||
finishLogin()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun finishLogin() {
|
||||
val progressDialog = MaterialDialog.Builder(this)
|
||||
.title("Finalizing login")
|
||||
.progress(true, 0)
|
||||
.content("Please wait...")
|
||||
.cancelable(false)
|
||||
.show()
|
||||
|
||||
val eh = sourceManager
|
||||
.getOnlineSources()
|
||||
.find { it.id == EXH_SOURCE_ID } as EHentai
|
||||
Observable.fromCallable {
|
||||
//I honestly have no idea why we need to call this twice, but it works, so whatever
|
||||
try {
|
||||
eh.fetchFavorites()
|
||||
} catch(ignored: Exception) {}
|
||||
try {
|
||||
eh.fetchFavorites()
|
||||
} catch(ignored: Exception) {}
|
||||
}.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
progressDialog.dismiss()
|
||||
onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we are logged in
|
||||
*/
|
||||
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z"/>
|
||||
</vector>
|
@ -22,9 +22,14 @@
|
||||
android:title="@string/action_update_library"
|
||||
app:showAsAction="ifRoom"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_sync"
|
||||
android:icon="@drawable/ic_cloud_download_white_24dp"
|
||||
android:title="Download favorites"
|
||||
app:showAsAction="ifRoom"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_edit_categories"
|
||||
android:title="@string/action_edit_categories"
|
||||
app:showAsAction="never"/>
|
||||
|
||||
</menu>
|
||||
|
@ -31,11 +31,10 @@
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreference
|
||||
android:dependency="enable_exhentai"
|
||||
android:defaultValue="true"
|
||||
android:key="secure_exh"
|
||||
android:title="Secure ExHentai"
|
||||
android:summary="Use the HTTPS version of ExHentai. Uncheck if ExHentai is not working" />
|
||||
android:title="Secure ExHentai/E-Hentai"
|
||||
android:summary="Use the HTTPS version of ExHentai/E-Hentai." />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="auto"
|
||||
|
Loading…
x
Reference in New Issue
Block a user