Minor improvements for sync services

This commit is contained in:
len 2016-05-31 16:08:46 +02:00
parent 02a697031f
commit 18cdddf433
12 changed files with 156 additions and 162 deletions

View file

@ -6,7 +6,7 @@ import com.pushtorefresh.storio.sqlite.annotations.StorIOSQLiteType;
import java.io.Serializable;
import eu.kanade.tachiyomi.data.database.tables.MangaSyncTable;
import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService;
import eu.kanade.tachiyomi.data.mangasync.MangaSyncService;
@StorIOSQLiteType(table = MangaSyncTable.TABLE)
public class MangaSync implements Serializable {

View file

@ -6,7 +6,7 @@ import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaSync
import eu.kanade.tachiyomi.data.database.tables.MangaSyncTable
import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService
import eu.kanade.tachiyomi.data.mangasync.MangaSyncService
interface MangaSyncQueries : DbProvider {

View file

@ -1,23 +1,18 @@
package eu.kanade.tachiyomi.data.mangasync
import android.content.Context
import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService
import eu.kanade.tachiyomi.data.mangasync.services.MyAnimeList
import eu.kanade.tachiyomi.data.mangasync.myanimelist.MyAnimeList
class MangaSyncManager(private val context: Context) {
val services: List<MangaSyncService>
val myAnimeList: MyAnimeList
companion object {
const val MYANIMELIST = 1
}
init {
myAnimeList = MyAnimeList(context, MYANIMELIST)
services = listOf(myAnimeList)
}
val myAnimeList = MyAnimeList(context, MYANIMELIST)
fun getService(id: Int): MangaSyncService = services.find { it.id == id }!!
val services = listOf(myAnimeList)
fun getService(id: Int) = services.find { it.id == id }
}

View file

@ -0,0 +1,56 @@
package eu.kanade.tachiyomi.data.mangasync
import android.content.Context
import android.support.annotation.CallSuper
import eu.kanade.tachiyomi.App
import eu.kanade.tachiyomi.data.database.models.MangaSync
import eu.kanade.tachiyomi.data.network.NetworkHelper
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import okhttp3.OkHttpClient
import rx.Completable
import rx.Observable
import javax.inject.Inject
abstract class MangaSyncService(private val context: Context, val id: Int) {
@Inject lateinit var preferences: PreferencesHelper
@Inject lateinit var networkService: NetworkHelper
init {
App.get(context).component.inject(this)
}
open val client: OkHttpClient
get() = networkService.client
// Name of the manga sync service to display
abstract val name: String
abstract fun login(username: String, password: String): Completable
open val isLogged: Boolean
get() = !getUsername().isEmpty() &&
!getPassword().isEmpty()
abstract fun add(manga: MangaSync): Observable<MangaSync>
abstract fun update(manga: MangaSync): Observable<MangaSync>
abstract fun bind(manga: MangaSync): Observable<MangaSync>
abstract fun getStatus(status: Int): String
fun saveCredentials(username: String, password: String) {
preferences.setMangaSyncCredentials(this, username, password)
}
@CallSuper
open fun logout() {
preferences.setMangaSyncCredentials(this, "", "")
}
fun getUsername() = preferences.mangaSyncUsername(this)
fun getPassword() = preferences.mangaSyncPassword(this)
}

View file

@ -48,15 +48,13 @@ class UpdateMangaSyncService : Service() {
private fun updateLastChapterRead(mangaSync: MangaSync, startId: Int) {
val sync = syncManager.getService(mangaSync.sync_id)
if (sync == null) {
stopSelf(startId)
return
}
subscriptions.add(Observable.defer { sync.update(mangaSync) }
.flatMap {
if (it.isSuccessful) {
db.insertMangaSync(mangaSync).asRxObservable()
} else {
Observable.error(Exception("Could not update manga in remote service"))
}
}
.flatMap { db.insertMangaSync(mangaSync).asRxObservable() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ stopSelf(startId) },

View file

@ -1,42 +0,0 @@
package eu.kanade.tachiyomi.data.mangasync.base
import android.content.Context
import eu.kanade.tachiyomi.App
import eu.kanade.tachiyomi.data.database.models.MangaSync
import eu.kanade.tachiyomi.data.network.NetworkHelper
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import okhttp3.OkHttpClient
import okhttp3.Response
import rx.Observable
import javax.inject.Inject
abstract class MangaSyncService(private val context: Context, val id: Int) {
@Inject lateinit var preferences: PreferencesHelper
@Inject lateinit var networkService: NetworkHelper
init {
App.get(context).component.inject(this)
}
open val client: OkHttpClient
get() = networkService.client
// Name of the manga sync service to display
abstract val name: String
abstract fun login(username: String, password: String): Observable<Boolean>
open val isLogged: Boolean
get() = !preferences.mangaSyncUsername(this).isEmpty() &&
!preferences.mangaSyncPassword(this).isEmpty()
abstract fun update(manga: MangaSync): Observable<Response>
abstract fun add(manga: MangaSync): Observable<Response>
abstract fun bind(manga: MangaSync): Observable<Response>
abstract fun getStatus(status: Int): String
}

View file

@ -1,26 +1,29 @@
package eu.kanade.tachiyomi.data.mangasync.services
package eu.kanade.tachiyomi.data.mangasync.myanimelist
import android.content.Context
import android.net.Uri
import android.util.Xml
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.MangaSync
import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService
import eu.kanade.tachiyomi.data.mangasync.MangaSyncService
import eu.kanade.tachiyomi.data.network.GET
import eu.kanade.tachiyomi.data.network.POST
import eu.kanade.tachiyomi.data.network.asObservable
import eu.kanade.tachiyomi.util.selectInt
import eu.kanade.tachiyomi.util.selectText
import okhttp3.*
import okhttp3.Credentials
import okhttp3.FormBody
import okhttp3.Headers
import okhttp3.RequestBody
import org.jsoup.Jsoup
import org.xmlpull.v1.XmlSerializer
import rx.Completable
import rx.Observable
import java.io.StringWriter
class MyAnimeList(private val context: Context, id: Int) : MangaSyncService(context, id) {
private lateinit var headers: Headers
private lateinit var username: String
companion object {
val BASE_URL = "http://myanimelist.net"
@ -41,8 +44,8 @@ class MyAnimeList(private val context: Context, id: Int) : MangaSyncService(cont
}
init {
val username = preferences.mangaSyncUsername(this)
val password = preferences.mangaSyncPassword(this)
val username = getUsername()
val password = getPassword()
if (!username.isEmpty() && !password.isEmpty()) {
createHeaders(username, password)
@ -52,25 +55,39 @@ class MyAnimeList(private val context: Context, id: Int) : MangaSyncService(cont
override val name: String
get() = "MyAnimeList"
fun getLoginUrl(): String {
return Uri.parse(BASE_URL).buildUpon()
.appendEncodedPath("api/account/verify_credentials.xml")
.toString()
}
fun getLoginUrl() = Uri.parse(BASE_URL).buildUpon()
.appendEncodedPath("api/account/verify_credentials.xml")
.toString()
override fun login(username: String, password: String): Observable<Boolean> {
fun getSearchUrl(query: String) = Uri.parse(BASE_URL).buildUpon()
.appendEncodedPath("api/manga/search.xml")
.appendQueryParameter("q", query)
.toString()
fun getListUrl(username: String) = Uri.parse(BASE_URL).buildUpon()
.appendPath("malappinfo.php")
.appendQueryParameter("u", username)
.appendQueryParameter("status", "all")
.appendQueryParameter("type", "manga")
.toString()
fun getUpdateUrl(manga: MangaSync) = Uri.parse(BASE_URL).buildUpon()
.appendEncodedPath("api/mangalist/update")
.appendPath("${manga.remote_id}.xml")
.toString()
fun getAddUrl(manga: MangaSync) = Uri.parse(BASE_URL).buildUpon()
.appendEncodedPath("api/mangalist/add")
.appendPath("${manga.remote_id}.xml")
.toString()
override fun login(username: String, password: String): Completable {
createHeaders(username, password)
return client.newCall(GET(getLoginUrl(), headers))
.asObservable()
.doOnNext { it.close() }
.map { it.code() == 200 }
}
fun getSearchUrl(query: String): String {
return Uri.parse(BASE_URL).buildUpon()
.appendEncodedPath("api/manga/search.xml")
.appendQueryParameter("q", query)
.toString()
.doOnNext { if (it.code() != 200) throw Exception("Login error") }
.toCompletable()
}
fun search(query: String): Observable<List<MangaSync>> {
@ -80,73 +97,56 @@ class MyAnimeList(private val context: Context, id: Int) : MangaSyncService(cont
.flatMap { Observable.from(it.select("entry")) }
.filter { it.select("type").text() != "Novel" }
.map {
val manga = MangaSync.create(this)
manga.title = it.selectText("title")
manga.remote_id = it.selectInt("id")
manga.total_chapters = it.selectInt("chapters")
manga
MangaSync.create(this).apply {
title = it.selectText("title")
remote_id = it.selectInt("id")
total_chapters = it.selectInt("chapters")
}
}
.toList()
}
fun getListUrl(username: String): String {
return Uri.parse(BASE_URL).buildUpon()
.appendPath("malappinfo.php")
.appendQueryParameter("u", username)
.appendQueryParameter("status", "all")
.appendQueryParameter("type", "manga")
.toString()
}
// MAL doesn't support score with decimals
fun getList(): Observable<List<MangaSync>> {
return networkService.forceCacheClient
.newCall(GET(getListUrl(username), headers))
.newCall(GET(getListUrl(getUsername()), headers))
.asObservable()
.map { Jsoup.parse(it.body().string()) }
.flatMap { Observable.from(it.select("manga")) }
.map {
val manga = MangaSync.create(this)
manga.title = it.selectText("series_title")
manga.remote_id = it.selectInt("series_mangadb_id")
manga.last_chapter_read = it.selectInt("my_read_chapters")
manga.status = it.selectInt("my_status")
manga.score = it.selectInt("my_score").toFloat()
manga.total_chapters = it.selectInt("series_chapters")
manga
MangaSync.create(this).apply {
title = it.selectText("series_title")
remote_id = it.selectInt("series_mangadb_id")
last_chapter_read = it.selectInt("my_read_chapters")
status = it.selectInt("my_status")
score = it.selectInt("my_score").toFloat()
total_chapters = it.selectInt("series_chapters")
}
}
.toList()
}
fun getUpdateUrl(manga: MangaSync): String {
return Uri.parse(BASE_URL).buildUpon()
.appendEncodedPath("api/mangalist/update")
.appendPath(manga.remote_id.toString() + ".xml")
.toString()
}
override fun update(manga: MangaSync): Observable<Response> {
override fun update(manga: MangaSync): Observable<MangaSync> {
return Observable.defer {
if (manga.total_chapters != 0 && manga.last_chapter_read == manga.total_chapters) {
manga.status = COMPLETED
}
client.newCall(POST(getUpdateUrl(manga), headers, getMangaPostPayload(manga)))
.asObservable()
.doOnNext { it.close() }
.doOnNext { if (!it.isSuccessful) throw Exception("Could not update manga") }
.map { manga }
}
}
fun getAddUrl(manga: MangaSync): String {
return Uri.parse(BASE_URL).buildUpon()
.appendEncodedPath("api/mangalist/add")
.appendPath(manga.remote_id.toString() + ".xml")
.toString()
}
override fun add(manga: MangaSync): Observable<Response> {
override fun add(manga: MangaSync): Observable<MangaSync> {
return Observable.defer {
client.newCall(POST(getAddUrl(manga), headers, getMangaPostPayload(manga)))
.asObservable()
.doOnNext { it.close() }
.doOnNext { if (!it.isSuccessful) throw Exception("Could not add manga") }
.map { manga }
}
}
@ -184,21 +184,20 @@ class MyAnimeList(private val context: Context, id: Int) : MangaSyncService(cont
endTag(namespace, tag)
}
override fun bind(manga: MangaSync): Observable<Response> {
override fun bind(manga: MangaSync): Observable<MangaSync> {
return getList()
.flatMap {
.flatMap { userlist ->
manga.sync_id = id
for (remoteManga in it) {
if (remoteManga.remote_id == manga.remote_id) {
// Manga is already in the list
manga.copyPersonalFrom(remoteManga)
return@flatMap update(manga)
}
val mangaFromList = userlist.find { it.remote_id == manga.remote_id }
if (mangaFromList != null) {
manga.copyPersonalFrom(mangaFromList)
update(manga)
} else {
// Set default fields if it's not found in the list
manga.score = DEFAULT_SCORE.toFloat()
manga.status = DEFAULT_STATUS
add(manga)
}
// Set default fields if it's not found in the list
manga.score = DEFAULT_SCORE.toFloat()
manga.status = DEFAULT_STATUS
return@flatMap add(manga)
}
}
@ -214,7 +213,6 @@ class MyAnimeList(private val context: Context, id: Int) : MangaSyncService(cont
}
fun createHeaders(username: String, password: String) {
this.username = username
val builder = Headers.Builder()
builder.add("Authorization", Credentials.basic(username, password))
builder.add("User-Agent", "api-indiv-9F93C52A963974CF674325391990191C")

View file

@ -6,7 +6,7 @@ import android.preference.PreferenceManager
import com.f2prateek.rx.preferences.Preference
import com.f2prateek.rx.preferences.RxSharedPreferences
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService
import eu.kanade.tachiyomi.data.mangasync.MangaSyncService
import eu.kanade.tachiyomi.data.source.Source
import java.io.File
import java.io.IOException

View file

@ -6,8 +6,8 @@ import eu.kanade.tachiyomi.data.download.DownloadService
import eu.kanade.tachiyomi.data.glide.AppGlideModule
import eu.kanade.tachiyomi.data.glide.MangaModelLoader
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
import eu.kanade.tachiyomi.data.mangasync.MangaSyncService
import eu.kanade.tachiyomi.data.mangasync.UpdateMangaSyncService
import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService
import eu.kanade.tachiyomi.data.source.Source
import eu.kanade.tachiyomi.data.source.online.OnlineSource
import eu.kanade.tachiyomi.data.updater.UpdateDownloader

View file

@ -98,7 +98,7 @@ class MyAnimeListPresenter : BasePresenter<MyAnimeListFragment>() {
mangaSync?.let { mangaSync ->
add(myAnimeList.update(mangaSync)
.subscribeOn(Schedulers.io())
.flatMap { response -> db.insertMangaSync(mangaSync).asRxObservable() }
.flatMap { db.insertMangaSync(mangaSync).asRxObservable() }
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ next -> },
{ error ->
@ -126,13 +126,7 @@ class MyAnimeListPresenter : BasePresenter<MyAnimeListFragment>() {
if (sync != null) {
sync.manga_id = manga.id
add(myAnimeList.bind(sync)
.flatMap { response ->
if (response.isSuccessful) {
db.insertMangaSync(sync).asRxObservable()
} else {
Observable.error(Exception("Could not bind manga"))
}
}
.flatMap { db.insertMangaSync(sync).asRxObservable() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ },

View file

@ -374,7 +374,7 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
fun updateMangaSyncLastChapterRead() {
for (mangaSync in mangaSyncList ?: emptyList()) {
val service = syncManager.getService(mangaSync.sync_id)
val service = syncManager.getService(mangaSync.sync_id) ?: continue
if (service.isLogged && mangaSync.update) {
UpdateMangaSyncService.start(context, mangaSync)
}

View file

@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.widget.preference
import android.os.Bundle
import android.view.View
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService
import eu.kanade.tachiyomi.data.mangasync.MangaSyncService
import eu.kanade.tachiyomi.ui.setting.SettingsActivity
import eu.kanade.tachiyomi.util.toast
import kotlinx.android.synthetic.main.pref_account_login.view.*
@ -29,13 +29,13 @@ class MangaSyncLoginDialog : LoginDialogPreference() {
super.onCreate(savedInstanceState)
val syncId = arguments.getInt("key")
sync = (activity as SettingsActivity).syncManager.getService(syncId)
sync = (activity as SettingsActivity).syncManager.getService(syncId)!!
}
override fun setCredentialsOnView(view: View) = with(view) {
dialog_title.text = getString(R.string.login_title, sync.name)
username.setText(preferences.mangaSyncUsername(sync))
password.setText(preferences.mangaSyncPassword(sync))
username.setText(sync.getUsername())
password.setText(sync.getPassword())
}
override fun checkLogin() {
@ -46,25 +46,20 @@ class MangaSyncLoginDialog : LoginDialogPreference() {
return
login.progress = 1
val user = username.text.toString()
val pass = password.text.toString()
requestSubscription = sync.login(username.text.toString(), password.text.toString())
requestSubscription = sync.login(user, pass)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ logged ->
if (logged) {
preferences.setMangaSyncCredentials(sync,
username.text.toString(),
password.text.toString())
dialog.dismiss()
context.toast(R.string.login_success)
} else {
preferences.setMangaSyncCredentials(sync, "", "")
login.progress = -1
}
}, { error ->
.subscribe({ error ->
sync.logout()
login.progress = -1
login.setText(R.string.unknown_error)
}, {
sync.saveCredentials(user, pass)
dialog.dismiss()
context.toast(R.string.login_success)
})
}