|
@@ -6,7 +6,19 @@ import com.jakewharton.rxrelay.PublishRelay
|
|
|
import eu.kanade.domain.chapter.model.Chapter
|
|
|
import eu.kanade.domain.chapter.model.toDbChapter
|
|
|
import eu.kanade.domain.download.service.DownloadPreferences
|
|
|
+import eu.kanade.domain.manga.model.ComicInfo
|
|
|
+import eu.kanade.domain.manga.model.ComicInfoGenre
|
|
|
+import eu.kanade.domain.manga.model.ComicInfoPenciller
|
|
|
+import eu.kanade.domain.manga.model.ComicInfoPublishingStatusTachiyomi
|
|
|
+import eu.kanade.domain.manga.model.ComicInfoSeries
|
|
|
+import eu.kanade.domain.manga.model.ComicInfoSummary
|
|
|
+import eu.kanade.domain.manga.model.ComicInfoTitle
|
|
|
+import eu.kanade.domain.manga.model.ComicInfoTranslator
|
|
|
+import eu.kanade.domain.manga.model.ComicInfoWeb
|
|
|
+import eu.kanade.domain.manga.model.ComicInfoWriter
|
|
|
import eu.kanade.domain.manga.model.Manga
|
|
|
+import eu.kanade.domain.track.interactor.GetTracks
|
|
|
+import eu.kanade.domain.track.model.Track
|
|
|
import eu.kanade.tachiyomi.R
|
|
|
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
|
|
import eu.kanade.tachiyomi.data.download.model.Download
|
|
@@ -16,6 +28,8 @@ import eu.kanade.tachiyomi.data.notification.NotificationHandler
|
|
|
import eu.kanade.tachiyomi.source.SourceManager
|
|
|
import eu.kanade.tachiyomi.source.UnmeteredSource
|
|
|
import eu.kanade.tachiyomi.source.model.Page
|
|
|
+import eu.kanade.tachiyomi.source.model.SChapter
|
|
|
+import eu.kanade.tachiyomi.source.model.SManga
|
|
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
|
|
import eu.kanade.tachiyomi.source.online.fetchAllImageUrlsFromPageList
|
|
|
import eu.kanade.tachiyomi.util.lang.RetryWithDelay
|
|
@@ -28,7 +42,9 @@ import eu.kanade.tachiyomi.util.storage.saveTo
|
|
|
import eu.kanade.tachiyomi.util.system.ImageUtil
|
|
|
import eu.kanade.tachiyomi.util.system.logcat
|
|
|
import kotlinx.coroutines.async
|
|
|
+import kotlinx.coroutines.runBlocking
|
|
|
import logcat.LogPriority
|
|
|
+import nl.adaptivity.xmlutil.serialization.XML
|
|
|
import okhttp3.Response
|
|
|
import rx.Observable
|
|
|
import rx.android.schedulers.AndroidSchedulers
|
|
@@ -36,8 +52,10 @@ import rx.schedulers.Schedulers
|
|
|
import rx.subscriptions.CompositeSubscription
|
|
|
import uy.kohesive.injekt.Injekt
|
|
|
import uy.kohesive.injekt.api.get
|
|
|
+import uy.kohesive.injekt.injectLazy
|
|
|
import java.io.BufferedOutputStream
|
|
|
import java.io.File
|
|
|
+import java.io.FileOutputStream
|
|
|
import java.util.zip.CRC32
|
|
|
import java.util.zip.ZipEntry
|
|
|
import java.util.zip.ZipOutputStream
|
|
@@ -63,8 +81,14 @@ class Downloader(
|
|
|
private val sourceManager: SourceManager = Injekt.get(),
|
|
|
private val chapterCache: ChapterCache = Injekt.get(),
|
|
|
private val downloadPreferences: DownloadPreferences = Injekt.get(),
|
|
|
+ private val getTracks: GetTracks = Injekt.get(),
|
|
|
) {
|
|
|
|
|
|
+ /**
|
|
|
+ * xml format used for ComicInfo files
|
|
|
+ */
|
|
|
+ private val xml: XML by injectLazy()
|
|
|
+
|
|
|
/**
|
|
|
* Store for persisting downloads across restarts.
|
|
|
*/
|
|
@@ -513,6 +537,8 @@ class Downloader(
|
|
|
// Ensure that the chapter folder has all the images.
|
|
|
val downloadedImages = tmpDir.listFiles().orEmpty().filterNot { it.name!!.endsWith(".tmp") || (it.name!!.contains("__") && !it.name!!.contains("__001.jpg")) }
|
|
|
|
|
|
+ createComicInfoFile(tmpDir, download.manga, download.chapter)
|
|
|
+
|
|
|
download.status = if (downloadedImages.size == download.pages!!.size) {
|
|
|
// Only rename the directory if it's downloaded.
|
|
|
if (downloadPreferences.saveChaptersAsCBZ().get()) {
|
|
@@ -524,6 +550,8 @@ class Downloader(
|
|
|
|
|
|
DiskUtil.createNoMediaFile(tmpDir, context)
|
|
|
|
|
|
+ createComicInfoFile(mangaDir, download.manga, download.chapter)
|
|
|
+
|
|
|
Download.State.DOWNLOADED
|
|
|
} else {
|
|
|
Download.State.ERROR
|
|
@@ -564,6 +592,59 @@ class Downloader(
|
|
|
tmpDir.delete()
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Creates a ComicInfo.xml file inside the given directory.
|
|
|
+ *
|
|
|
+ * @param dir the directory in which the ComicInfo file will be generated.
|
|
|
+ * @param manga the manga of the chapter to download.
|
|
|
+ * @param chapter the chapter to download
|
|
|
+ */
|
|
|
+ private fun createComicInfoFile(
|
|
|
+ dir: UniFile,
|
|
|
+ manga: Manga,
|
|
|
+ chapter: SChapter,
|
|
|
+ ) {
|
|
|
+ File("${dir.filePath}/ComicInfo.xml").outputStream().also {
|
|
|
+ // Force overwrite old file
|
|
|
+ (it as? FileOutputStream)?.channel?.truncate(0)
|
|
|
+ }.use { it.write(getComicInfo(manga, chapter)) }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * returns a ByteArray containing the Manga Metadata of the chapter to download in ComicInfo.xml format
|
|
|
+ *
|
|
|
+ * @param manga the manga of the chapter to download.
|
|
|
+ * @param chapter the name of the chapter to download
|
|
|
+ */
|
|
|
+ private fun getComicInfo(manga: Manga, chapter: SChapter): ByteArray {
|
|
|
+ val track: Track? = runBlocking { getTracks.await(manga.id).firstOrNull() }
|
|
|
+ val comicInfo = ComicInfo(
|
|
|
+ title = ComicInfoTitle(chapter.name),
|
|
|
+ series = ComicInfoSeries(manga.title),
|
|
|
+ summary = manga.description?.let { ComicInfoSummary(it) },
|
|
|
+ writer = manga.author?.let { ComicInfoWriter(it) },
|
|
|
+ penciller = manga.artist?.let { ComicInfoPenciller(it) },
|
|
|
+ translator = chapter.scanlator?.let { ComicInfoTranslator(it) },
|
|
|
+ genre = manga.genre?.let { ComicInfoGenre(it.joinToString()) },
|
|
|
+ web = track?.remoteUrl?.let { ComicInfoWeb(it) },
|
|
|
+ publishingStatusTachiyomi = when (manga.status) {
|
|
|
+ SManga.ONGOING.toLong() -> ComicInfoPublishingStatusTachiyomi("Ongoing")
|
|
|
+ SManga.COMPLETED.toLong() -> ComicInfoPublishingStatusTachiyomi("Completed")
|
|
|
+ SManga.LICENSED.toLong() -> ComicInfoPublishingStatusTachiyomi("Licensed")
|
|
|
+ SManga.PUBLISHING_FINISHED.toLong() -> ComicInfoPublishingStatusTachiyomi("Publishing finished")
|
|
|
+ SManga.CANCELLED.toLong() -> ComicInfoPublishingStatusTachiyomi("Cancelled")
|
|
|
+ SManga.ON_HIATUS.toLong() -> ComicInfoPublishingStatusTachiyomi("On hiatus")
|
|
|
+ else -> ComicInfoPublishingStatusTachiyomi("Unknown")
|
|
|
+ },
|
|
|
+ inker = null,
|
|
|
+ colorist = null,
|
|
|
+ letterer = null,
|
|
|
+ coverArtist = null,
|
|
|
+ tags = null,
|
|
|
+ )
|
|
|
+ return xml.encodeToString(ComicInfo.serializer(), comicInfo).toByteArray()
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Completes a download. This method is called in the main thread.
|
|
|
*/
|