Allow to refresh the entire library info (fixing empty covers after restoring backups). Closes #462
This commit is contained in:
parent
500eedaab7
commit
1f70be688a
9 changed files with 83 additions and 15 deletions
|
@ -71,18 +71,18 @@ class LibraryUpdateService : Service() {
|
|||
private val notificationId: Int
|
||||
get() = Constants.NOTIFICATION_LIBRARY_ID
|
||||
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Key for manual library update.
|
||||
*/
|
||||
const val UPDATE_IS_MANUAL = "is_manual"
|
||||
|
||||
/**
|
||||
* Key for category to update.
|
||||
*/
|
||||
const val UPDATE_CATEGORY = "category"
|
||||
|
||||
/**
|
||||
* Key for updating the details instead of the chapters.
|
||||
*/
|
||||
const val UPDATE_DETAILS = "details"
|
||||
|
||||
/**
|
||||
* Returns the status of the service.
|
||||
*
|
||||
|
@ -98,13 +98,13 @@ class LibraryUpdateService : Service() {
|
|||
* running.
|
||||
*
|
||||
* @param context the application context.
|
||||
* @param isManual whether the update has been manually triggered.
|
||||
* @param category a specific category to update, or null for global update.
|
||||
* @param details whether to update the details instead of the list of chapters.
|
||||
*/
|
||||
fun start(context: Context, isManual: Boolean = false, category: Category? = null) {
|
||||
fun start(context: Context, category: Category? = null, details: Boolean = false) {
|
||||
if (!isRunning(context)) {
|
||||
val intent = Intent(context, LibraryUpdateService::class.java).apply {
|
||||
putExtra(UPDATE_IS_MANUAL, isManual)
|
||||
putExtra(UPDATE_DETAILS, details)
|
||||
category?.let { putExtra(UPDATE_CATEGORY, it.id) }
|
||||
}
|
||||
context.startService(intent)
|
||||
|
@ -164,7 +164,16 @@ class LibraryUpdateService : Service() {
|
|||
subscription?.unsubscribe()
|
||||
|
||||
// Update favorite manga. Destroy service when completed or in case of an error.
|
||||
subscription = Observable.defer { updateMangaList(getMangaToUpdate(intent)) }
|
||||
subscription = Observable
|
||||
.defer {
|
||||
val mangaList = getMangaToUpdate(intent)
|
||||
|
||||
// Update either chapter list or manga details.
|
||||
if (!intent.getBooleanExtra(UPDATE_DETAILS, false))
|
||||
updateChapterList(mangaList)
|
||||
else
|
||||
updateDetails(mangaList)
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe({
|
||||
}, {
|
||||
|
@ -216,7 +225,7 @@ class LibraryUpdateService : Service() {
|
|||
* @param mangaToUpdate the list to update
|
||||
* @return an observable delivering the progress of each update.
|
||||
*/
|
||||
fun updateMangaList(mangaToUpdate: List<Manga>): Observable<Manga> {
|
||||
fun updateChapterList(mangaToUpdate: List<Manga>): Observable<Manga> {
|
||||
// Initialize the variables holding the progress of the updates.
|
||||
val count = AtomicInteger(0)
|
||||
val newUpdates = ArrayList<Manga>()
|
||||
|
@ -266,6 +275,41 @@ class LibraryUpdateService : Service() {
|
|||
.map { syncChaptersWithSource(db, it, manga, source) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that updates the details of the given list of manga. It's called in a background
|
||||
* thread, so it's safe to do heavy operations or network calls here.
|
||||
* For each manga it calls [updateManga] and updates the notification showing the current
|
||||
* progress.
|
||||
*
|
||||
* @param mangaToUpdate the list to update
|
||||
* @return an observable delivering the progress of each update.
|
||||
*/
|
||||
fun updateDetails(mangaToUpdate: List<Manga>): Observable<Manga> {
|
||||
// Initialize the variables holding the progress of the updates.
|
||||
val count = AtomicInteger(0)
|
||||
|
||||
val cancelIntent = PendingIntent.getBroadcast(this, 0,
|
||||
Intent(this, CancelUpdateReceiver::class.java), 0)
|
||||
|
||||
// Emit each manga and update it sequentially.
|
||||
return Observable.from(mangaToUpdate)
|
||||
// Notify manga that will update.
|
||||
.doOnNext { showProgressNotification(it, count.andIncrement, mangaToUpdate.size, cancelIntent) }
|
||||
// Update the details of the manga.
|
||||
.concatMap { manga ->
|
||||
val source = sourceManager.get(manga.source) as? OnlineSource
|
||||
?: return@concatMap Observable.empty<Manga>()
|
||||
|
||||
source.fetchMangaDetails(manga).doOnNext { networkManga ->
|
||||
manga.copyFrom(networkManga)
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
}
|
||||
}
|
||||
.doOnCompleted {
|
||||
cancelNotification()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text that will be displayed in the notification when there are new chapters.
|
||||
*
|
||||
|
|
|
@ -101,7 +101,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
|||
swipe_refresh.setDistanceToTriggerSync((2 * 64 * resources.displayMetrics.density).toInt())
|
||||
swipe_refresh.setOnRefreshListener {
|
||||
if (!LibraryUpdateService.isRunning(context)) {
|
||||
LibraryUpdateService.start(context, true, category)
|
||||
LibraryUpdateService.start(context, category)
|
||||
context.toast(R.string.updating_category)
|
||||
}
|
||||
// It can be a very long operation, so we disable swipe refresh and show a toast.
|
||||
|
|
|
@ -241,7 +241,7 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
|
|||
}
|
||||
R.id.action_library_display_mode -> swapDisplayMode()
|
||||
R.id.action_update_library -> {
|
||||
LibraryUpdateService.start(activity, true)
|
||||
LibraryUpdateService.start(activity)
|
||||
}
|
||||
R.id.action_edit_categories -> {
|
||||
val intent = CategoryActivity.newIntent(activity)
|
||||
|
|
|
@ -7,6 +7,7 @@ import com.afollestad.materialdialogs.MaterialDialog
|
|||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
||||
import eu.kanade.tachiyomi.data.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.util.plusAssign
|
||||
import eu.kanade.tachiyomi.util.toast
|
||||
|
@ -38,6 +39,8 @@ class SettingsAdvancedFragment : SettingsFragment() {
|
|||
|
||||
private val clearCookies by lazy { findPreference(getString(R.string.pref_clear_cookies_key)) }
|
||||
|
||||
private val refreshMetadata by lazy { findPreference(getString(R.string.pref_refresh_library_metadata_key)) }
|
||||
|
||||
override fun onViewCreated(view: View, savedState: Bundle?) {
|
||||
super.onViewCreated(view, savedState)
|
||||
|
||||
|
@ -57,6 +60,11 @@ class SettingsAdvancedFragment : SettingsFragment() {
|
|||
clearDatabase()
|
||||
true
|
||||
}
|
||||
|
||||
refreshMetadata.setOnPreferenceClickListener {
|
||||
LibraryUpdateService.start(context, details = true)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearChapterCache() {
|
||||
|
|
|
@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.widget.preference.LibraryColumnsDialog
|
|||
import eu.kanade.tachiyomi.widget.preference.SimpleDialogPreference
|
||||
import net.xpece.android.support.preference.MultiSelectListPreference
|
||||
import rx.Observable
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class SettingsGeneralFragment : SettingsFragment(),
|
||||
|
@ -76,6 +77,15 @@ class SettingsGeneralFragment : SettingsFragment(),
|
|||
true
|
||||
}
|
||||
|
||||
updateRestriction.setOnPreferenceChangeListener { preference, newValue ->
|
||||
// Post to event looper to allow the preference to be updated.
|
||||
subscriptions += Observable.fromCallable {
|
||||
LibraryUpdateTrigger.setupTask(context)
|
||||
}.subscribeOn(AndroidSchedulers.mainThread()).subscribe()
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
val dbCategories = db.getCategories().executeAsBlocking()
|
||||
categoryUpdate.apply {
|
||||
entries = dbCategories.map { it.name }.toTypedArray()
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
<string name="pref_clear_chapter_cache_key">pref_clear_chapter_cache_key</string>
|
||||
<string name="pref_clear_database_key">pref_clear_database_key</string>
|
||||
<string name="pref_clear_cookies_key">pref_clear_cookies_key</string>
|
||||
<string name="pref_refresh_library_metadata_key">refresh_library_metadata</string>
|
||||
|
||||
<string name="pref_version">pref_version</string>
|
||||
<string name="pref_build_time">pref_build_time</string>
|
||||
|
|
|
@ -176,8 +176,8 @@
|
|||
<string name="pref_clear_database_summary">Delete manga and chapters that are not in your library</string>
|
||||
<string name="clear_database_confirmation">Are you sure? Read chapters and progress of non-library manga will be lost</string>
|
||||
<string name="clear_database_completed">Entries deleted</string>
|
||||
<string name="pref_show_warning_message">Show warnings</string>
|
||||
<string name="pref_show_warning_message_summary">Show warning messages during library sync </string>
|
||||
<string name="pref_refresh_library_metadata">Refresh library metadata</string>
|
||||
<string name="pref_refresh_library_metadata_summary">Updates covers, genres, description and manga status information</string>
|
||||
<string name="pref_reencode">Reencode images</string>
|
||||
<string name="pref_reencode_summary">Enable reencoding if images can\'t be decoded. Expect best results with Skia</string>
|
||||
|
||||
|
|
|
@ -20,6 +20,11 @@
|
|||
android:summary="@string/pref_clear_database_summary"
|
||||
android:title="@string/pref_clear_database"/>
|
||||
|
||||
<Preference
|
||||
android:key="@string/pref_refresh_library_metadata_key"
|
||||
android:summary="@string/pref_refresh_library_metadata_summary"
|
||||
android:title="@string/pref_refresh_library_metadata"/>
|
||||
|
||||
<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="@string/pref_reencode_key"
|
||||
|
|
|
@ -95,7 +95,7 @@ class LibraryUpdateServiceTest {
|
|||
`when`(source.fetchChapterList(favManga[2])).thenReturn(Observable.just(chapters3))
|
||||
|
||||
val intent = Intent()
|
||||
service.updateMangaList(service.getMangaToUpdate(intent)).subscribe()
|
||||
service.updateChapterList(service.getMangaToUpdate(intent)).subscribe()
|
||||
|
||||
// There are 3 network attempts and 2 insertions (1 request failed)
|
||||
assertThat(service.db.getChapters(favManga[0]).executeAsBlocking()).hasSize(2)
|
||||
|
|
Reference in a new issue