mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-19 01:59:19 +01:00
Allow to refresh the entire library info (fixing empty covers after restoring backups). Closes #462
This commit is contained in:
parent
500eedaab7
commit
1f70be688a
@ -71,18 +71,18 @@ class LibraryUpdateService : Service() {
|
|||||||
private val notificationId: Int
|
private val notificationId: Int
|
||||||
get() = Constants.NOTIFICATION_LIBRARY_ID
|
get() = Constants.NOTIFICATION_LIBRARY_ID
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
|
||||||
* Key for manual library update.
|
|
||||||
*/
|
|
||||||
const val UPDATE_IS_MANUAL = "is_manual"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key for category to update.
|
* Key for category to update.
|
||||||
*/
|
*/
|
||||||
const val UPDATE_CATEGORY = "category"
|
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.
|
* Returns the status of the service.
|
||||||
*
|
*
|
||||||
@ -98,13 +98,13 @@ class LibraryUpdateService : Service() {
|
|||||||
* running.
|
* running.
|
||||||
*
|
*
|
||||||
* @param context the application context.
|
* @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 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)) {
|
if (!isRunning(context)) {
|
||||||
val intent = Intent(context, LibraryUpdateService::class.java).apply {
|
val intent = Intent(context, LibraryUpdateService::class.java).apply {
|
||||||
putExtra(UPDATE_IS_MANUAL, isManual)
|
putExtra(UPDATE_DETAILS, details)
|
||||||
category?.let { putExtra(UPDATE_CATEGORY, it.id) }
|
category?.let { putExtra(UPDATE_CATEGORY, it.id) }
|
||||||
}
|
}
|
||||||
context.startService(intent)
|
context.startService(intent)
|
||||||
@ -164,7 +164,16 @@ class LibraryUpdateService : Service() {
|
|||||||
subscription?.unsubscribe()
|
subscription?.unsubscribe()
|
||||||
|
|
||||||
// Update favorite manga. Destroy service when completed or in case of an error.
|
// 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())
|
.subscribeOn(Schedulers.io())
|
||||||
.subscribe({
|
.subscribe({
|
||||||
}, {
|
}, {
|
||||||
@ -216,7 +225,7 @@ class LibraryUpdateService : Service() {
|
|||||||
* @param mangaToUpdate the list to update
|
* @param mangaToUpdate the list to update
|
||||||
* @return an observable delivering the progress of each 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.
|
// Initialize the variables holding the progress of the updates.
|
||||||
val count = AtomicInteger(0)
|
val count = AtomicInteger(0)
|
||||||
val newUpdates = ArrayList<Manga>()
|
val newUpdates = ArrayList<Manga>()
|
||||||
@ -266,6 +275,41 @@ class LibraryUpdateService : Service() {
|
|||||||
.map { syncChaptersWithSource(db, it, manga, source) }
|
.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.
|
* 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.setDistanceToTriggerSync((2 * 64 * resources.displayMetrics.density).toInt())
|
||||||
swipe_refresh.setOnRefreshListener {
|
swipe_refresh.setOnRefreshListener {
|
||||||
if (!LibraryUpdateService.isRunning(context)) {
|
if (!LibraryUpdateService.isRunning(context)) {
|
||||||
LibraryUpdateService.start(context, true, category)
|
LibraryUpdateService.start(context, category)
|
||||||
context.toast(R.string.updating_category)
|
context.toast(R.string.updating_category)
|
||||||
}
|
}
|
||||||
// It can be a very long operation, so we disable swipe refresh and show a toast.
|
// 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_library_display_mode -> swapDisplayMode()
|
||||||
R.id.action_update_library -> {
|
R.id.action_update_library -> {
|
||||||
LibraryUpdateService.start(activity, true)
|
LibraryUpdateService.start(activity)
|
||||||
}
|
}
|
||||||
R.id.action_edit_categories -> {
|
R.id.action_edit_categories -> {
|
||||||
val intent = CategoryActivity.newIntent(activity)
|
val intent = CategoryActivity.newIntent(activity)
|
||||||
|
@ -7,6 +7,7 @@ import com.afollestad.materialdialogs.MaterialDialog
|
|||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
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.data.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.util.plusAssign
|
import eu.kanade.tachiyomi.util.plusAssign
|
||||||
import eu.kanade.tachiyomi.util.toast
|
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 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?) {
|
override fun onViewCreated(view: View, savedState: Bundle?) {
|
||||||
super.onViewCreated(view, savedState)
|
super.onViewCreated(view, savedState)
|
||||||
|
|
||||||
@ -57,6 +60,11 @@ class SettingsAdvancedFragment : SettingsFragment() {
|
|||||||
clearDatabase()
|
clearDatabase()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshMetadata.setOnPreferenceClickListener {
|
||||||
|
LibraryUpdateService.start(context, details = true)
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun clearChapterCache() {
|
private fun clearChapterCache() {
|
||||||
|
@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.widget.preference.LibraryColumnsDialog
|
|||||||
import eu.kanade.tachiyomi.widget.preference.SimpleDialogPreference
|
import eu.kanade.tachiyomi.widget.preference.SimpleDialogPreference
|
||||||
import net.xpece.android.support.preference.MultiSelectListPreference
|
import net.xpece.android.support.preference.MultiSelectListPreference
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
class SettingsGeneralFragment : SettingsFragment(),
|
class SettingsGeneralFragment : SettingsFragment(),
|
||||||
@ -76,6 +77,15 @@ class SettingsGeneralFragment : SettingsFragment(),
|
|||||||
true
|
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()
|
val dbCategories = db.getCategories().executeAsBlocking()
|
||||||
categoryUpdate.apply {
|
categoryUpdate.apply {
|
||||||
entries = dbCategories.map { it.name }.toTypedArray()
|
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_chapter_cache_key">pref_clear_chapter_cache_key</string>
|
||||||
<string name="pref_clear_database_key">pref_clear_database_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_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_version">pref_version</string>
|
||||||
<string name="pref_build_time">pref_build_time</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="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_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="clear_database_completed">Entries deleted</string>
|
||||||
<string name="pref_show_warning_message">Show warnings</string>
|
<string name="pref_refresh_library_metadata">Refresh library metadata</string>
|
||||||
<string name="pref_show_warning_message_summary">Show warning messages during library sync </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">Reencode images</string>
|
||||||
<string name="pref_reencode_summary">Enable reencoding if images can\'t be decoded. Expect best results with Skia</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:summary="@string/pref_clear_database_summary"
|
||||||
android:title="@string/pref_clear_database"/>
|
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
|
<SwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="@string/pref_reencode_key"
|
android:key="@string/pref_reencode_key"
|
||||||
|
@ -95,7 +95,7 @@ class LibraryUpdateServiceTest {
|
|||||||
`when`(source.fetchChapterList(favManga[2])).thenReturn(Observable.just(chapters3))
|
`when`(source.fetchChapterList(favManga[2])).thenReturn(Observable.just(chapters3))
|
||||||
|
|
||||||
val intent = Intent()
|
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)
|
// There are 3 network attempts and 2 insertions (1 request failed)
|
||||||
assertThat(service.db.getChapters(favManga[0]).executeAsBlocking()).hasSize(2)
|
assertThat(service.db.getChapters(favManga[0]).executeAsBlocking()).hasSize(2)
|
||||||
|
Loading…
Reference in New Issue
Block a user