Răsfoiți Sursa

Restore tracking on backup (#1097)

Bram van de Kerkhof 7 ani în urmă
părinte
comite
e745836404

+ 42 - 40
app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt

@@ -23,6 +23,7 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
 import eu.kanade.tachiyomi.data.database.models.*
 import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.data.preference.getOrDefault
+import eu.kanade.tachiyomi.data.track.TrackManager
 import eu.kanade.tachiyomi.source.Source
 import eu.kanade.tachiyomi.source.SourceManager
 import eu.kanade.tachiyomi.util.syncChaptersWithSource
@@ -41,6 +42,11 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
      */
     internal val sourceManager: SourceManager by injectLazy()
 
+    /**
+     * Tracking manager
+     */
+    internal val trackManager: TrackManager by injectLazy()
+
     /**
      * Version of parser
      */
@@ -67,18 +73,16 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
         parser = initParser()
     }
 
-    private fun initParser(): Gson {
-        return when (version) {
-            1 -> GsonBuilder().create()
-            2 -> GsonBuilder()
-                    .registerTypeAdapter<MangaImpl>(MangaTypeAdapter.build())
-                    .registerTypeHierarchyAdapter<ChapterImpl>(ChapterTypeAdapter.build())
-                    .registerTypeAdapter<CategoryImpl>(CategoryTypeAdapter.build())
-                    .registerTypeAdapter<DHistory>(HistoryTypeAdapter.build())
-                    .registerTypeHierarchyAdapter<TrackImpl>(TrackTypeAdapter.build())
-                    .create()
-            else -> throw Exception("Json version unknown")
-        }
+    private fun initParser(): Gson = when (version) {
+        1 -> GsonBuilder().create()
+        2 -> GsonBuilder()
+                .registerTypeAdapter<MangaImpl>(MangaTypeAdapter.build())
+                .registerTypeHierarchyAdapter<ChapterImpl>(ChapterTypeAdapter.build())
+                .registerTypeAdapter<CategoryImpl>(CategoryTypeAdapter.build())
+                .registerTypeAdapter<DHistory>(HistoryTypeAdapter.build())
+                .registerTypeHierarchyAdapter<TrackImpl>(TrackTypeAdapter.build())
+                .create()
+        else -> throw Exception("Json version unknown")
     }
 
     /**
@@ -300,23 +304,26 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
         val trackToUpdate = ArrayList<Track>()
 
         for (track in tracks) {
-            var isInDatabase = false
-            for (dbTrack in dbTracks) {
-                if (track.sync_id == dbTrack.sync_id) {
-                    // The sync is already in the db, only update its fields
-                    if (track.remote_id != dbTrack.remote_id) {
-                        dbTrack.remote_id = track.remote_id
+            val service = trackManager.getService(track.sync_id)
+            if (service != null && service.isLogged) {
+                var isInDatabase = false
+                for (dbTrack in dbTracks) {
+                    if (track.sync_id == dbTrack.sync_id) {
+                        // The sync is already in the db, only update its fields
+                        if (track.remote_id != dbTrack.remote_id) {
+                            dbTrack.remote_id = track.remote_id
+                        }
+                        dbTrack.last_chapter_read = Math.max(dbTrack.last_chapter_read, track.last_chapter_read)
+                        isInDatabase = true
+                        trackToUpdate.add(dbTrack)
+                        break
                     }
-                    dbTrack.last_chapter_read = Math.max(dbTrack.last_chapter_read, track.last_chapter_read)
-                    isInDatabase = true
-                    trackToUpdate.add(dbTrack)
-                    break
                 }
-            }
-            if (!isInDatabase) {
-                // Insert new sync. Let the db assign the id
-                track.id = null
-                trackToUpdate.add(track)
+                if (!isInDatabase) {
+                    // Insert new sync. Let the db assign the id
+                    track.id = null
+                    trackToUpdate.add(track)
+                }
             }
         }
         // Update database
@@ -361,32 +368,29 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
      *
      * @return [Manga], null if not found
      */
-    internal fun getMangaFromDatabase(manga: Manga): Manga? {
-        return databaseHelper.getManga(manga.url, manga.source).executeAsBlocking()
-    }
+    internal fun getMangaFromDatabase(manga: Manga): Manga? =
+            databaseHelper.getManga(manga.url, manga.source).executeAsBlocking()
 
     /**
      * Returns list containing manga from library
      *
      * @return [Manga] from library
      */
-    internal fun getFavoriteManga(): List<Manga> {
-        return databaseHelper.getFavoriteMangas().executeAsBlocking()
-    }
+    internal fun getFavoriteManga(): List<Manga> =
+            databaseHelper.getFavoriteMangas().executeAsBlocking()
 
     /**
      * Inserts manga and returns id
      *
      * @return id of [Manga], null if not found
      */
-    internal fun insertManga(manga: Manga): Long? {
-        return databaseHelper.insertManga(manga).executeAsBlocking().insertedId()
-    }
+    internal fun insertManga(manga: Manga): Long? =
+            databaseHelper.insertManga(manga).executeAsBlocking().insertedId()
 
     /**
      * Inserts list of chapters
      */
-    internal fun insertChapters(chapters: List<Chapter>) {
+    private fun insertChapters(chapters: List<Chapter>) {
         databaseHelper.updateChaptersBackup(chapters).executeAsBlocking()
     }
 
@@ -395,7 +399,5 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
      *
      * @return number of backups selected by user
      */
-    fun numberOfBackups(): Int {
-        return preferences.numberOfBackups().getOrDefault()
-    }
+    fun numberOfBackups(): Int = preferences.numberOfBackups().getOrDefault()
 }

+ 62 - 28
app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt

@@ -21,6 +21,7 @@ import eu.kanade.tachiyomi.data.backup.models.Backup.VERSION
 import eu.kanade.tachiyomi.data.backup.models.DHistory
 import eu.kanade.tachiyomi.data.database.DatabaseHelper
 import eu.kanade.tachiyomi.data.database.models.*
+import eu.kanade.tachiyomi.data.track.TrackManager
 import eu.kanade.tachiyomi.source.Source
 import eu.kanade.tachiyomi.util.chop
 import eu.kanade.tachiyomi.util.isServiceRunning
@@ -49,9 +50,8 @@ class BackupRestoreService : Service() {
          * @param context the application context.
          * @return true if the service is running, false otherwise.
          */
-        fun isRunning(context: Context): Boolean {
-            return context.isServiceRunning(BackupRestoreService::class.java)
-        }
+        private fun isRunning(context: Context): Boolean =
+                context.isServiceRunning(BackupRestoreService::class.java)
 
         /**
          * Starts a service to restore a backup from Json
@@ -113,7 +113,13 @@ class BackupRestoreService : Service() {
      */
     private val db: DatabaseHelper by injectLazy()
 
-    lateinit var executor: ExecutorService
+    /**
+     * Tracking manager
+     */
+    internal val trackManager: TrackManager by injectLazy()
+
+
+    private lateinit var executor: ExecutorService
 
     /**
      * Method called when the service is created. It injects dependencies and acquire the wake lock.
@@ -142,9 +148,7 @@ class BackupRestoreService : Service() {
     /**
      * This method needs to be implemented, but it's not used/needed.
      */
-    override fun onBind(intent: Intent): IBinder? {
-        return null
-    }
+    override fun onBind(intent: Intent): IBinder? = null
 
     /**
      * Method called when the service receives an intent.
@@ -164,7 +168,7 @@ class BackupRestoreService : Service() {
 
         subscription = Observable.using(
                 { db.lowLevel().beginTransaction() },
-                { getRestoreObservable(uri).doOnNext{ db.lowLevel().setTransactionSuccessful() } },
+                { getRestoreObservable(uri).doOnNext { db.lowLevel().setTransactionSuccessful() } },
                 { executor.execute { db.lowLevel().endTransaction() } })
                 .doAfterTerminate { stopSelf(startId) }
                 .subscribeOn(Schedulers.from(executor))
@@ -294,14 +298,14 @@ class BackupRestoreService : Service() {
         val source = backupManager.sourceManager.get(manga.source) ?: return null
         val dbManga = backupManager.getMangaFromDatabase(manga)
 
-        if (dbManga == null) {
+        return if (dbManga == null) {
             // Manga not in database
-            return mangaFetchObservable(source, manga, chapters, categories, history, tracks)
+            mangaFetchObservable(source, manga, chapters, categories, history, tracks)
         } else { // Manga in database
             // Copy information from manga already in database
             backupManager.restoreMangaNoFetch(manga, dbManga)
             // Fetch rest of manga information
-            return mangaNoFetchObservable(source, manga, chapters, categories, history, tracks)
+            mangaNoFetchObservable(source, manga, chapters, categories, history, tracks)
         }
     }
 
@@ -327,14 +331,12 @@ class BackupRestoreService : Service() {
                             .map { manga }
                 }
                 .doOnNext {
-                    // Restore categories
-                    backupManager.restoreCategoriesForManga(it, categories)
-
-                    // Restore history
-                    backupManager.restoreHistoryForManga(history)
-
-                    // Restore tracking
-                    backupManager.restoreTrackForManga(it, tracks)
+                    restoreExtraForManga(it, categories, history, tracks)
+                }
+                .flatMap {
+                    trackingFetchObservable(it, tracks)
+                            // Convert to the manga that contains new chapters.
+                            .map { manga }
                 }
                 .doOnCompleted {
                     restoreProgress += 1
@@ -356,14 +358,12 @@ class BackupRestoreService : Service() {
                     }
                 }
                 .doOnNext {
-                    // Restore categories
-                    backupManager.restoreCategoriesForManga(it, categories)
-
-                    // Restore history
-                    backupManager.restoreHistoryForManga(history)
-
-                    // Restore tracking
-                    backupManager.restoreTrackForManga(it, tracks)
+                    restoreExtraForManga(it, categories, history, tracks)
+                }
+                .flatMap { manga ->
+                    trackingFetchObservable(manga, tracks)
+                            // Convert to the manga that contains new chapters.
+                            .map { manga }
                 }
                 .doOnCompleted {
                     restoreProgress += 1
@@ -371,6 +371,17 @@ class BackupRestoreService : Service() {
                 }
     }
 
+    private fun restoreExtraForManga(manga: Manga, categories: List<String>, history: List<DHistory>, tracks: List<Track>) {
+        // Restore categories
+        backupManager.restoreCategoriesForManga(manga, categories)
+
+        // Restore history
+        backupManager.restoreHistoryForManga(history)
+
+        // Restore tracking
+        backupManager.restoreTrackForManga(manga, tracks)
+    }
+
     /**
      * [Observable] that fetches chapter information
      *
@@ -383,10 +394,33 @@ class BackupRestoreService : Service() {
                 // If there's any error, return empty update and continue.
                 .onErrorReturn {
                     errors.add(Date() to "${manga.title} - ${it.message}")
-                    Pair(emptyList<Chapter>(), emptyList<Chapter>())
+                    Pair(emptyList(), emptyList())
                 }
     }
 
+    /**
+     * [Observable] that refreshes tracking information
+     * @param manga manga that needs updating.
+     * @param tracks list containing tracks from restore file.
+     * @return [Observable] that contains updated track item
+     */
+    private fun trackingFetchObservable(manga: Manga, tracks: List<Track>): Observable<Track> {
+        return Observable.from(tracks)
+                .concatMap { track ->
+                    val service = trackManager.getService(track.sync_id)
+                    if (service != null && service.isLogged) {
+                        service.refresh(track)
+                                .doOnNext { db.insertTrack(it).executeAsBlocking() }
+                                .onErrorReturn {
+                                    errors.add(Date() to "${manga.title} - ${it.message}")
+                                    track
+                                }
+                    } else {
+                        errors.add(Date() to "${manga.title} - ${service?.name} not logged in")
+                        Observable.empty()
+                    }
+                }
+    }
 
     /**
      * Called to update dialog in [BackupConst]

+ 1 - 1
app/src/main/res/values-de/strings.xml

@@ -201,7 +201,7 @@
     <string name="dialog_restoring_backup">Backup wird wiederhergestellt
 %1$s zur Bibliothek hinzugefügt</string>
     <string name="source_not_found">Quelle nicht gefunden</string>
-    <string name="dialog_restoring_source_not_found">Backup wird wiederhergestellt %1%s
+    <string name="dialog_restoring_source_not_found">Backup wird wiederhergestellt %1$s
 \nQuelle nicht gefunden</string>
     <string name="backup_created">Backup erstellt</string>
     <string name="restore_completed">Wiederherstellen erfolgreich</string>