package eu.kanade.tachiyomi.source import android.content.Context import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import rx.Observable // import com.github.junrar.Archive // import com.google.gson.JsonParser // import eu.kanade.tachiyomi.R // import eu.kanade.tachiyomi.source.model.Filter // import eu.kanade.tachiyomi.source.model.FilterList // import eu.kanade.tachiyomi.source.model.MangasPage // 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.util.chapter.ChapterRecognition // import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder // import eu.kanade.tachiyomi.util.storage.DiskUtil // import eu.kanade.tachiyomi.util.storage.EpubFile // import eu.kanade.tachiyomi.util.system.ImageUtil // import rx.Observable // import timber.log.Timber // import java.io.File // import java.io.FileInputStream // import java.io.InputStream // import java.util.Locale // import java.util.concurrent.TimeUnit // import java.util.zip.ZipFile class LocalSource(private val context: Context) : CatalogueSource { companion object { const val ID = 0L // const val HELP_URL = "https://tachiyomi.org/help/guides/reading-local-manga/" // // private const val COVER_NAME = "cover.jpg" // private val SUPPORTED_ARCHIVE_TYPES = setOf("zip", "rar", "cbr", "cbz", "epub") // // private val POPULAR_FILTERS = FilterList(OrderBy()) // private val LATEST_FILTERS = FilterList(OrderBy().apply { state = Filter.Sort.Selection(1, false) }) // private val LATEST_THRESHOLD = TimeUnit.MILLISECONDS.convert(7, TimeUnit.DAYS) // // fun updateCover(context: Context, manga: SManga, input: InputStream): File? { // val dir = getBaseDirectories(context).firstOrNull() // if (dir == null) { // input.close() // return null // } // val cover = File("${dir.absolutePath}/${manga.url}", COVER_NAME) // // // It might not exist if using the external SD card // cover.parentFile?.mkdirs() // input.use { // cover.outputStream().use { // input.copyTo(it) // } // } // return cover // } // // private fun getBaseDirectories(context: Context): List { // val c = context.getString(R.string.app_name) + File.separator + "local" // return DiskUtil.getExternalStorages(context).map { File(it.absolutePath, c) } // } } override val id = ID override val name = "Local source" override val lang = "" override val supportsLatest = true override fun fetchMangaDetails(manga: SManga): Observable { TODO("Not yet implemented") } override fun fetchChapterList(manga: SManga): Observable> { TODO("Not yet implemented") } override fun fetchPageList(chapter: SChapter): Observable> { TODO("Not yet implemented") } override fun fetchPopularManga(page: Int): Observable { TODO("Not yet implemented") } override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { TODO("Not yet implemented") } override fun fetchLatestUpdates(page: Int): Observable { TODO("Not yet implemented") } override fun getFilterList(): FilterList { TODO("Not yet implemented") } // // override fun toString() = context.getString(R.string.local_source) // // override fun fetchPopularManga(page: Int) = fetchSearchManga(page, "", POPULAR_FILTERS) // // override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { // val baseDirs = getBaseDirectories(context) // // val time = if (filters === LATEST_FILTERS) System.currentTimeMillis() - LATEST_THRESHOLD else 0L // var mangaDirs = baseDirs // .asSequence() // .mapNotNull { it.listFiles()?.toList() } // .flatten() // .filter { it.isDirectory } // .filterNot { it.name.startsWith('.') } // .filter { if (time == 0L) it.name.contains(query, ignoreCase = true) else it.lastModified() >= time } // .distinctBy { it.name } // // val state = ((if (filters.isEmpty()) POPULAR_FILTERS else filters)[0] as OrderBy).state // when (state?.index) { // 0 -> { // mangaDirs = if (state.ascending) { // mangaDirs.sortedBy { it.name.toLowerCase(Locale.ENGLISH) } // } else { // mangaDirs.sortedByDescending { it.name.toLowerCase(Locale.ENGLISH) } // } // } // 1 -> { // mangaDirs = if (state.ascending) { // mangaDirs.sortedBy(File::lastModified) // } else { // mangaDirs.sortedByDescending(File::lastModified) // } // } // } // // val mangas = mangaDirs.map { mangaDir -> // SManga.create().apply { // title = mangaDir.name // url = mangaDir.name // // // Try to find the cover // for (dir in baseDirs) { // val cover = File("${dir.absolutePath}/$url", COVER_NAME) // if (cover.exists()) { // thumbnail_url = cover.absolutePath // break // } // } // // val chapters = fetchChapterList(this).toBlocking().first() // if (chapters.isNotEmpty()) { // val chapter = chapters.last() // val format = getFormat(chapter) // if (format is Format.Epub) { // EpubFile(format.file).use { epub -> // epub.fillMangaMetadata(this) // } // } // // // Copy the cover from the first chapter found. // if (thumbnail_url == null) { // try { // val dest = updateCover(chapter, this) // thumbnail_url = dest?.absolutePath // } catch (e: Exception) { // Timber.e(e) // } // } // } // } // } // // return Observable.just(MangasPage(mangas.toList(), false)) // } // // override fun fetchLatestUpdates(page: Int) = fetchSearchManga(page, "", LATEST_FILTERS) // // override fun fetchMangaDetails(manga: SManga): Observable { // getBaseDirectories(context) // .asSequence() // .mapNotNull { File(it, manga.url).listFiles()?.toList() } // .flatten() // .firstOrNull { it.extension == "json" } // ?.apply { // val reader = this.inputStream().bufferedReader() // val json = JsonParser.parseReader(reader).asJsonObject // // manga.title = json["title"]?.asString ?: manga.title // manga.author = json["author"]?.asString ?: manga.author // manga.artist = json["artist"]?.asString ?: manga.artist // manga.description = json["description"]?.asString ?: manga.description // manga.genre = json["genre"]?.asJsonArray?.joinToString(", ") { it.asString } // ?: manga.genre // manga.status = json["status"]?.asInt ?: manga.status // } // // return Observable.just(manga) // } // // override fun fetchChapterList(manga: SManga): Observable> { // val chapters = getBaseDirectories(context) // .asSequence() // .mapNotNull { File(it, manga.url).listFiles()?.toList() } // .flatten() // .filter { it.isDirectory || isSupportedFile(it.extension) } // .map { chapterFile -> // SChapter.create().apply { // url = "${manga.url}/${chapterFile.name}" // name = if (chapterFile.isDirectory) { // chapterFile.name // } else { // chapterFile.nameWithoutExtension // } // date_upload = chapterFile.lastModified() // // val format = getFormat(this) // if (format is Format.Epub) { // EpubFile(format.file).use { epub -> // epub.fillChapterMetadata(this) // } // } // // val chapNameCut = stripMangaTitle(name, manga.title) // if (chapNameCut.isNotEmpty()) name = chapNameCut // ChapterRecognition.parseChapterNumber(this, manga) // } // } // .sortedWith( // Comparator { c1, c2 -> // val c = c2.chapter_number.compareTo(c1.chapter_number) // if (c == 0) c2.name.compareToCaseInsensitiveNaturalOrder(c1.name) else c // } // ) // .toList() // // return Observable.just(chapters) // } // // /** // * Strips the manga title from a chapter name, matching only based on alphanumeric and whitespace // * characters. // */ // private fun stripMangaTitle(chapterName: String, mangaTitle: String): String { // var chapterNameIndex = 0 // var mangaTitleIndex = 0 // while (chapterNameIndex < chapterName.length && mangaTitleIndex < mangaTitle.length) { // val chapterChar = chapterName[chapterNameIndex] // val mangaChar = mangaTitle[mangaTitleIndex] // if (!chapterChar.equals(mangaChar, true)) { // val invalidChapterChar = !chapterChar.isLetterOrDigit() && !chapterChar.isWhitespace() // val invalidMangaChar = !mangaChar.isLetterOrDigit() && !mangaChar.isWhitespace() // // if (!invalidChapterChar && !invalidMangaChar) { // return chapterName // } // // if (invalidChapterChar) { // chapterNameIndex++ // } // // if (invalidMangaChar) { // mangaTitleIndex++ // } // } else { // chapterNameIndex++ // mangaTitleIndex++ // } // } // // return chapterName.substring(chapterNameIndex).trimStart(' ', '-', '_', ',', ':') // } // // override fun fetchPageList(chapter: SChapter): Observable> { // return Observable.error(Exception("Unused")) // } // // private fun isSupportedFile(extension: String): Boolean { // return extension.toLowerCase() in SUPPORTED_ARCHIVE_TYPES // } // // fun getFormat(chapter: SChapter): Format { // val baseDirs = getBaseDirectories(context) // // for (dir in baseDirs) { // val chapFile = File(dir, chapter.url) // if (!chapFile.exists()) continue // // return getFormat(chapFile) // } // throw Exception("Chapter not found") // } // // private fun getFormat(file: File): Format { // val extension = file.extension // return if (file.isDirectory) { // Format.Directory(file) // } else if (extension.equals("zip", true) || extension.equals("cbz", true)) { // Format.Zip(file) // } else if (extension.equals("rar", true) || extension.equals("cbr", true)) { // Format.Rar(file) // } else if (extension.equals("epub", true)) { // Format.Epub(file) // } else { // throw Exception("Invalid chapter format") // } // } // // private fun updateCover(chapter: SChapter, manga: SManga): File? { // return when (val format = getFormat(chapter)) { // is Format.Directory -> { // val entry = format.file.listFiles() // ?.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) } // ?.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } } // // entry?.let { updateCover(context, manga, it.inputStream()) } // } // is Format.Zip -> { // ZipFile(format.file).use { zip -> // val entry = zip.entries().toList() // .sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) } // .find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } } // // entry?.let { updateCover(context, manga, zip.getInputStream(it)) } // } // } // is Format.Rar -> { // Archive(format.file).use { archive -> // val entry = archive.fileHeaders // .sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) } // .find { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } } // // entry?.let { updateCover(context, manga, archive.getInputStream(it)) } // } // } // is Format.Epub -> { // EpubFile(format.file).use { epub -> // val entry = epub.getImagesFromPages() // .firstOrNull() // ?.let { epub.getEntry(it) } // // entry?.let { updateCover(context, manga, epub.getInputStream(it)) } // } // } // } // } // // private class OrderBy : Filter.Sort("Order by", arrayOf("Title", "Date"), Selection(0, true)) // // override fun getFilterList() = FilterList(OrderBy()) // // sealed class Format { // data class Directory(val file: File) : Format() // data class Zip(val file: File) : Format() // data class Rar(val file: File) : Format() // data class Epub(val file: File) : Format() // } }