mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 16:18:55 +01:00 
			
		
		
		
	Allow to refresh the entire library info (fixing empty covers after restoring backups). Closes #462
This commit is contained in:
		@@ -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 New Issue
	
	Block a user