@ -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 .
* /