mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 06:17:57 +01:00 
			
		
		
		
	Minor cleanup
- Remove some unused StorIO queries - Clean up tall image splitting a bit (no need for creating an unscaled scaled bitmap copy, or tracking coordinates) - Clean up library updater a bit (still needs a lot of work though)
This commit is contained in:
		| @@ -72,12 +72,8 @@ interface ChapterQueries : DbProvider { | ||||
|         ) | ||||
|         .prepare() | ||||
|  | ||||
|     fun insertChapter(chapter: Chapter) = db.put().`object`(chapter).prepare() | ||||
|  | ||||
|     fun insertChapters(chapters: List<Chapter>) = db.put().objects(chapters).prepare() | ||||
|  | ||||
|     fun deleteChapter(chapter: Chapter) = db.delete().`object`(chapter).prepare() | ||||
|  | ||||
|     fun deleteChapters(chapters: List<Chapter>) = db.delete().objects(chapters).prepare() | ||||
|  | ||||
|     fun updateChaptersBackup(chapters: List<Chapter>) = db.put() | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| package eu.kanade.tachiyomi.data.database.queries | ||||
|  | ||||
| import com.pushtorefresh.storio.sqlite.queries.DeleteQuery | ||||
| import com.pushtorefresh.storio.sqlite.queries.RawQuery | ||||
| import eu.kanade.tachiyomi.data.database.DbProvider | ||||
| import eu.kanade.tachiyomi.data.database.models.History | ||||
| @@ -50,14 +49,4 @@ interface HistoryQueries : DbProvider { | ||||
|         .objects(historyList) | ||||
|         .withPutResolver(HistoryUpsertResolver()) | ||||
|         .prepare() | ||||
|  | ||||
|     fun deleteHistoryNoLastRead() = db.delete() | ||||
|         .byQuery( | ||||
|             DeleteQuery.builder() | ||||
|                 .table(HistoryTable.TABLE) | ||||
|                 .where("${HistoryTable.COL_LAST_READ} = ?") | ||||
|                 .whereArgs(0) | ||||
|                 .build(), | ||||
|         ) | ||||
|         .prepare() | ||||
| } | ||||
|   | ||||
| @@ -131,10 +131,6 @@ interface MangaQueries : DbProvider { | ||||
|         .withPutResolver(MangaCoverLastModifiedPutResolver()) | ||||
|         .prepare() | ||||
|  | ||||
|     fun deleteManga(manga: Manga) = db.delete().`object`(manga).prepare() | ||||
|  | ||||
|     fun deleteMangas(mangas: List<Manga>) = db.delete().objects(mangas).prepare() | ||||
|  | ||||
|     fun deleteMangasNotInLibraryBySourceIds(sourceIds: List<Long>) = db.delete() | ||||
|         .byQuery( | ||||
|             DeleteQuery.builder() | ||||
| @@ -145,14 +141,6 @@ interface MangaQueries : DbProvider { | ||||
|         ) | ||||
|         .prepare() | ||||
|  | ||||
|     fun deleteMangas() = db.delete() | ||||
|         .byQuery( | ||||
|             DeleteQuery.builder() | ||||
|                 .table(MangaTable.TABLE) | ||||
|                 .build(), | ||||
|         ) | ||||
|         .prepare() | ||||
|  | ||||
|     fun getLastReadManga() = db.get() | ||||
|         .listOfObjects(Manga::class.java) | ||||
|         .withQuery( | ||||
|   | ||||
| @@ -4,7 +4,6 @@ import android.content.Context | ||||
| import android.graphics.Bitmap | ||||
| import android.graphics.BitmapFactory | ||||
| import android.webkit.MimeTypeMap | ||||
| import androidx.core.graphics.BitmapCompat | ||||
| import com.hippo.unifile.UniFile | ||||
| import com.jakewharton.rxrelay.BehaviorRelay | ||||
| import com.jakewharton.rxrelay.PublishRelay | ||||
| @@ -44,7 +43,6 @@ import uy.kohesive.injekt.injectLazy | ||||
| import java.io.BufferedOutputStream | ||||
| import java.io.File | ||||
| import java.io.FileOutputStream | ||||
| import java.io.OutputStream | ||||
| import java.util.zip.CRC32 | ||||
| import java.util.zip.ZipEntry | ||||
| import java.util.zip.ZipOutputStream | ||||
| @@ -354,7 +352,7 @@ class Downloader( | ||||
|             // Do when page is downloaded. | ||||
|             .doOnNext { page -> | ||||
|                 if (preferences.splitTallImages().get()) { | ||||
|                     splitTallImage(page, download, tmpDir) | ||||
|                     splitTallImage(page, tmpDir) | ||||
|                 } | ||||
|                 notifier.onProgressChange(download) | ||||
|             } | ||||
| @@ -560,51 +558,39 @@ class Downloader( | ||||
|     /** | ||||
|      * Splits tall images to improve performance of reader | ||||
|      */ | ||||
|     private fun splitTallImage(page: Page, download: Download, tmpDir: UniFile) { | ||||
|     private fun splitTallImage(page: Page, tmpDir: UniFile) { | ||||
|         val filename = String.format("%03d", page.number) | ||||
|         val imageFile = tmpDir.listFiles()!!.find { it.name!!.startsWith("$filename.") } | ||||
|         if (imageFile == null) { | ||||
|             notifier.onError("Error: imageFile was not found", download.chapter.name, download.manga.title) | ||||
|         val imageFile = tmpDir.listFiles()?.find { it.name!!.startsWith("$filename.") } | ||||
|             ?: throw Error(context.getString(R.string.download_notifier_split_page_not_found, page.number)) | ||||
|  | ||||
|         if (isAnimatedAndSupported(imageFile.openInputStream()) || !isTallImage(imageFile.openInputStream())) { | ||||
|             return | ||||
|         } | ||||
|  | ||||
|         if (!isAnimatedAndSupported(imageFile.openInputStream()) && isTallImage(imageFile.openInputStream())) { | ||||
|             // Getting the scaled bitmap of the source image | ||||
|             val bitmap = BitmapFactory.decodeFile(imageFile.filePath) | ||||
|             val scaledBitmap: Bitmap = | ||||
|                 BitmapCompat.createScaledBitmap(bitmap, bitmap.width, bitmap.height, null, true) | ||||
|         val bitmap = BitmapFactory.decodeFile(imageFile.filePath) | ||||
|         val splitsCount = bitmap.height / context.resources.displayMetrics.heightPixels + 1 | ||||
|         val heightPerSplit = bitmap.height / splitsCount | ||||
|  | ||||
|             val splitsCount: Int = bitmap.height / context.resources.displayMetrics.heightPixels + 1 | ||||
|             val splitHeight = bitmap.height / splitsCount | ||||
|  | ||||
|             // xCoord and yCoord are the pixel positions of the image splits | ||||
|             val xCoord = 0 | ||||
|             var yCoord = 0 | ||||
|             try { | ||||
|                 for (i in 0 until splitsCount) { | ||||
|                     val splitPath = imageFile.filePath!!.substringBeforeLast(".") + "__${"%03d".format(i + 1)}.jpg" | ||||
|                     // Compress the bitmap and save in jpg format | ||||
|                     val stream: OutputStream = FileOutputStream(splitPath) | ||||
|                     stream.use { | ||||
|                         Bitmap.createBitmap( | ||||
|                             scaledBitmap, | ||||
|                             xCoord, | ||||
|                             yCoord, | ||||
|                             bitmap.width, | ||||
|                             splitHeight, | ||||
|                         ).compress(Bitmap.CompressFormat.JPEG, 100, stream) | ||||
|                     } | ||||
|                     yCoord += splitHeight | ||||
|         try { | ||||
|             (0..splitsCount).forEach { split -> | ||||
|                 val splitPath = imageFile.filePath!!.substringBeforeLast(".") + "__${"%03d".format(split + 1)}.jpg" | ||||
|                 FileOutputStream(splitPath).use { stream -> | ||||
|                     Bitmap.createBitmap( | ||||
|                         bitmap, | ||||
|                         0, | ||||
|                         split * heightPerSplit, | ||||
|                         bitmap.width, | ||||
|                         heightPerSplit, | ||||
|                     ).compress(Bitmap.CompressFormat.JPEG, 100, stream) | ||||
|                 } | ||||
|                 imageFile.delete() | ||||
|             } catch (e: Exception) { | ||||
|                 // Image splits were not successfully saved so delete them and keep the original image | ||||
|                 for (i in 0 until splitsCount) { | ||||
|                     val splitPath = imageFile.filePath!!.substringBeforeLast(".") + "__${"%03d".format(i + 1)}.jpg" | ||||
|                     File(splitPath).delete() | ||||
|                 } | ||||
|                 throw e | ||||
|             } | ||||
|             imageFile.delete() | ||||
|         } catch (e: Exception) { | ||||
|             // Image splits were not successfully saved so delete them and keep the original image | ||||
|             (0..splitsCount) | ||||
|                 .map { imageFile.filePath!!.substringBeforeLast(".") + "__${"%03d".format(it + 1)}.jpg" } | ||||
|                 .forEach { File(it).delete() } | ||||
|             throw e | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -28,6 +28,7 @@ import eu.kanade.tachiyomi.data.track.TrackService | ||||
| import eu.kanade.tachiyomi.source.SourceManager | ||||
| import eu.kanade.tachiyomi.source.UnmeteredSource | ||||
| import eu.kanade.tachiyomi.source.model.SManga | ||||
| import eu.kanade.tachiyomi.source.model.toMangaInfo | ||||
| import eu.kanade.tachiyomi.source.model.toSChapter | ||||
| import eu.kanade.tachiyomi.source.model.toSManga | ||||
| import eu.kanade.tachiyomi.util.chapter.NoChaptersException | ||||
| @@ -80,7 +81,7 @@ class LibraryUpdateService( | ||||
|  | ||||
|     private lateinit var wakeLock: PowerManager.WakeLock | ||||
|     private lateinit var notifier: LibraryUpdateNotifier | ||||
|     private lateinit var ioScope: CoroutineScope | ||||
|     private var ioScope: CoroutineScope? = null | ||||
|  | ||||
|     private var mangaToUpdate: List<LibraryManga> = mutableListOf() | ||||
|     private var updateJob: Job? = null | ||||
| @@ -90,10 +91,8 @@ class LibraryUpdateService( | ||||
|      */ | ||||
|     enum class Target { | ||||
|         CHAPTERS, // Manga chapters | ||||
|  | ||||
|         COVERS, // Manga covers | ||||
|  | ||||
|         TRACKING // Tracking metadata | ||||
|         TRACKING, // Tracking metadata | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
| @@ -161,7 +160,6 @@ class LibraryUpdateService( | ||||
|     override fun onCreate() { | ||||
|         super.onCreate() | ||||
|  | ||||
|         ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) | ||||
|         notifier = LibraryUpdateNotifier(this) | ||||
|         wakeLock = acquireWakeLock(javaClass.name) | ||||
|  | ||||
| @@ -174,8 +172,6 @@ class LibraryUpdateService( | ||||
|      */ | ||||
|     override fun onDestroy() { | ||||
|         updateJob?.cancel() | ||||
|         // Despite what Android Studio | ||||
|         // states this can be null | ||||
|         ioScope?.cancel() | ||||
|         if (wakeLock.isHeld) { | ||||
|             wakeLock.release() | ||||
| @@ -189,9 +185,7 @@ class LibraryUpdateService( | ||||
|     /** | ||||
|      * 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. | ||||
| @@ -210,6 +204,7 @@ class LibraryUpdateService( | ||||
|  | ||||
|         // Unsubscribe from any previous subscription if needed | ||||
|         updateJob?.cancel() | ||||
|         ioScope?.cancel() | ||||
|  | ||||
|         // Update favorite manga | ||||
|         val categoryId = intent.getIntExtra(KEY_CATEGORY, -1) | ||||
| @@ -220,7 +215,8 @@ class LibraryUpdateService( | ||||
|             logcat(LogPriority.ERROR, exception) | ||||
|             stopSelf(startId) | ||||
|         } | ||||
|         updateJob = ioScope.launch(handler) { | ||||
|         ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) | ||||
|         updateJob = ioScope?.launch(handler) { | ||||
|             when (target) { | ||||
|                 Target.CHAPTERS -> updateChapterList() | ||||
|                 Target.COVERS -> updateCovers() | ||||
| @@ -344,16 +340,10 @@ class LibraryUpdateService( | ||||
|                                         } | ||||
|                                     } catch (e: Throwable) { | ||||
|                                         val errorMessage = when (e) { | ||||
|                                             is NoChaptersException -> { | ||||
|                                                 getString(R.string.no_chapters_error) | ||||
|                                             } | ||||
|                                             is SourceManager.SourceNotInstalledException -> { | ||||
|                                                 // failedUpdates will already have the source, don't need to copy it into the message | ||||
|                                                 getString(R.string.loader_not_implemented_error) | ||||
|                                             } | ||||
|                                             else -> { | ||||
|                                                 e.message | ||||
|                                             } | ||||
|                                             is NoChaptersException -> getString(R.string.no_chapters_error) | ||||
|                                             // failedUpdates will already have the source, don't need to copy it into the message | ||||
|                                             is SourceManager.SourceNotInstalledException -> getString(R.string.loader_not_implemented_error) | ||||
|                                             else -> e.message | ||||
|                                         } | ||||
|                                         failedUpdates.add(mangaWithNotif to errorMessage) | ||||
|                                     } | ||||
| @@ -407,11 +397,12 @@ class LibraryUpdateService( | ||||
|     private suspend fun updateManga(manga: Manga): Pair<List<Chapter>, List<Chapter>> { | ||||
|         val source = sourceManager.getOrStub(manga.source) | ||||
|  | ||||
|         var networkSManga: SManga? = null | ||||
|         var updatedManga: SManga = manga | ||||
|  | ||||
|         // Update manga details metadata | ||||
|         if (preferences.autoUpdateMetadata()) { | ||||
|             val updatedManga = source.getMangaDetails(manga.toMangaInfo()) | ||||
|             val sManga = updatedManga.toSManga() | ||||
|             val updatedMangaDetails = source.getMangaDetails(manga.toMangaInfo()) | ||||
|             val sManga = updatedMangaDetails.toSManga() | ||||
|             // Avoid "losing" existing cover | ||||
|             if (!sManga.thumbnail_url.isNullOrEmpty()) { | ||||
|                 manga.prepUpdateCover(coverCache, sManga, false) | ||||
| @@ -419,25 +410,22 @@ class LibraryUpdateService( | ||||
|                 sManga.thumbnail_url = manga.thumbnail_url | ||||
|             } | ||||
|  | ||||
|             networkSManga = sManga | ||||
|             updatedManga = sManga | ||||
|         } | ||||
|  | ||||
|         val chapters = source.getChapterList(manga.toMangaInfo()) | ||||
|         val chapters = source.getChapterList(updatedManga.toMangaInfo()) | ||||
|             .map { it.toSChapter() } | ||||
|  | ||||
|         // Get manga from database to account for if it was removed | ||||
|         // from library or database | ||||
|         // Get manga from database to account for if it was removed during the update | ||||
|         val dbManga = db.getManga(manga.id!!).executeAsBlocking() | ||||
|             ?: return Pair(emptyList(), emptyList()) | ||||
|  | ||||
|         // Copy into [dbManga] to retain favourite value | ||||
|         networkSManga?.let { | ||||
|             dbManga.copyFrom(it) | ||||
|             db.insertManga(dbManga).executeAsBlocking() | ||||
|         } | ||||
|         dbManga.copyFrom(updatedManga) | ||||
|         db.insertManga(dbManga).executeAsBlocking() | ||||
|  | ||||
|         // [dbmanga] was used so that manga data doesn't get overwritten | ||||
|         // incase manga gets new chapter | ||||
|         // in case manga gets new chapter | ||||
|         return syncChaptersWithSource(db, chapters, dbManga, source) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -247,7 +247,7 @@ class PagerPageHolder( | ||||
|             return splitInHalf(imageStream) | ||||
|         } | ||||
|  | ||||
|         val isDoublePage = ImageUtil.isDoublePage(imageStream) | ||||
|         val isDoublePage = ImageUtil.isWideImage(imageStream) | ||||
|         if (!isDoublePage) { | ||||
|             return imageStream | ||||
|         } | ||||
|   | ||||
| @@ -277,7 +277,7 @@ class WebtoonPageHolder( | ||||
|             return imageStream | ||||
|         } | ||||
|  | ||||
|         val isDoublePage = ImageUtil.isDoublePage(imageStream) | ||||
|         val isDoublePage = ImageUtil.isWideImage(imageStream) | ||||
|         if (!isDoublePage) { | ||||
|             return imageStream | ||||
|         } | ||||
|   | ||||
| @@ -99,38 +99,24 @@ object ImageUtil { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check whether the image is a double-page spread | ||||
|      * Check whether the image is wide (which we consider a double-page spread). | ||||
|      * | ||||
|      * @return true if the width is greater than the height | ||||
|      */ | ||||
|     fun isDoublePage(imageStream: InputStream): Boolean { | ||||
|         imageStream.mark(imageStream.available() + 1) | ||||
|  | ||||
|         val imageBytes = imageStream.readBytes() | ||||
|  | ||||
|         val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } | ||||
|         BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, options) | ||||
|  | ||||
|     fun isWideImage(imageStream: InputStream): Boolean { | ||||
|         val options = extractImageOptions(imageStream) | ||||
|         imageStream.reset() | ||||
|  | ||||
|         return options.outWidth > options.outHeight | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check whether the image is considered a tall image | ||||
|      * @return true if the height:width ratio is greater than the 3:! | ||||
|      * Check whether the image is considered a tall image. | ||||
|      * | ||||
|      * @return true if the height:width ratio is greater than 3. | ||||
|      */ | ||||
|     fun isTallImage(imageStream: InputStream): Boolean { | ||||
|         imageStream.mark(imageStream.available() + 1) | ||||
|  | ||||
|         val imageBytes = imageStream.readBytes() | ||||
|         // Checking the image dimensions without loading it in the memory. | ||||
|         val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } | ||||
|         BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, options) | ||||
|         val width = options.outWidth | ||||
|         val height = options.outHeight | ||||
|         val ratio = height / width | ||||
|  | ||||
|         return ratio > 3 | ||||
|         val options = extractImageOptions(imageStream) | ||||
|         return (options.outHeight / options.outWidth) > 3 | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -410,4 +396,16 @@ object ImageUtil { | ||||
|  | ||||
|     private fun Int.isWhite(): Boolean = | ||||
|         red + blue + green > 740 | ||||
|  | ||||
|     /** | ||||
|      * Used to check an image's dimensions without loading it in the memory. | ||||
|      */ | ||||
|     private fun extractImageOptions(imageStream: InputStream): BitmapFactory.Options { | ||||
|         imageStream.mark(imageStream.available() + 1) | ||||
|  | ||||
|         val imageBytes = imageStream.readBytes() | ||||
|         val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } | ||||
|         BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, options) | ||||
|         return options | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -814,6 +814,7 @@ | ||||
|     <string name="download_notifier_no_network">No network connection available</string> | ||||
|     <string name="download_notifier_download_paused">Download paused</string> | ||||
|     <string name="download_notifier_download_finish">Download completed</string> | ||||
|     <string name="download_notifier_split_page_not_found">Page %d not found while splitting</string> | ||||
|  | ||||
|     <!-- Notification channels --> | ||||
|     <string name="channel_common">Common</string> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user