mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01:00 
			
		
		
		
	Rar/cbr support
This commit is contained in:
		| @@ -6,7 +6,10 @@ import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.source.model.* | ||||
| import eu.kanade.tachiyomi.util.ChapterRecognition | ||||
| import eu.kanade.tachiyomi.util.DiskUtil | ||||
| import eu.kanade.tachiyomi.util.RarContentProvider | ||||
| import eu.kanade.tachiyomi.util.ZipContentProvider | ||||
| import junrar.Archive | ||||
| import junrar.rarfile.FileHeader | ||||
| import net.greypanther.natsort.CaseInsensitiveSimpleNaturalComparator | ||||
| import org.jsoup.Jsoup | ||||
| import org.jsoup.nodes.Document | ||||
| @@ -169,7 +172,9 @@ class LocalSource(private val context: Context) : CatalogueSource { | ||||
|     } | ||||
|  | ||||
|     private fun isSupportedFormat(extension: String): Boolean { | ||||
|         return extension.equals("zip", true) || extension.equals("cbz", true) || extension.equals("epub", true) | ||||
|         return extension.equals("zip", true) || extension.equals("cbz", true) | ||||
|                 || extension.equals("rar", true) || extension.equals("cbr", true) | ||||
|                 || extension.equals("epub", true) | ||||
|     } | ||||
|  | ||||
|     private fun getLoader(file: File): Loader { | ||||
| @@ -180,6 +185,8 @@ class LocalSource(private val context: Context) : CatalogueSource { | ||||
|             ZipLoader(file) | ||||
|         } else if (extension.equals("epub", true)) { | ||||
|             EpubLoader(file) | ||||
|         } else if (extension.equals("rar", true) || extension.equals("cbr", true)) { | ||||
|             RarLoader(file) | ||||
|         } else { | ||||
|             throw Exception("Invalid chapter format") | ||||
|         } | ||||
| @@ -207,8 +214,8 @@ class LocalSource(private val context: Context) : CatalogueSource { | ||||
|     class ZipLoader(val file: File) : Loader { | ||||
|         override fun load(): List<Page> { | ||||
|             val comparator = CaseInsensitiveSimpleNaturalComparator.getInstance<String>() | ||||
|             ZipFile(file).use { zip -> | ||||
|                 return zip.entries().toList() | ||||
|             return ZipFile(file).use { zip -> | ||||
|                 zip.entries().toList() | ||||
|                         .filter { !it.isDirectory && DiskUtil.isImage(it.name, { zip.getInputStream(it) }) } | ||||
|                         .sortedWith(Comparator<ZipEntry> { f1, f2 -> comparator.compare(f1.name, f2.name) }) | ||||
|                         .map { Uri.parse("content://${ZipContentProvider.PROVIDER}${file.absolutePath}!/${it.name}") } | ||||
| @@ -217,6 +224,19 @@ class LocalSource(private val context: Context) : CatalogueSource { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     class RarLoader(val file: File) : Loader { | ||||
|         override fun load(): List<Page> { | ||||
|             val comparator = CaseInsensitiveSimpleNaturalComparator.getInstance<String>() | ||||
|             return Archive(file).use { archive -> | ||||
|                 archive.fileHeaders | ||||
|                         .filter { !it.isDirectory && DiskUtil.isImage(it.fileNameString, { archive.getInputStream(it) }) } | ||||
|                         .sortedWith(Comparator<FileHeader> { f1, f2 -> comparator.compare(f1.fileNameString, f2.fileNameString) }) | ||||
|                         .map { Uri.parse("content://${RarContentProvider.PROVIDER}${file.absolutePath}!-/${it.fileNameString}") } | ||||
|                         .mapIndexed { i, uri -> Page(i, uri = uri).apply { status = Page.READY } } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     class EpubLoader(val file: File) : Loader { | ||||
|  | ||||
|         override fun load(): List<Page> { | ||||
|   | ||||
| @@ -0,0 +1,73 @@ | ||||
| package eu.kanade.tachiyomi.util | ||||
|  | ||||
| import android.content.ContentProvider | ||||
| import android.content.ContentValues | ||||
| import android.content.res.AssetFileDescriptor | ||||
| import android.database.Cursor | ||||
| import android.net.Uri | ||||
| import android.os.ParcelFileDescriptor | ||||
| import eu.kanade.tachiyomi.BuildConfig | ||||
| import junrar.Archive | ||||
| import java.io.File | ||||
| import java.io.IOException | ||||
| import java.net.URLConnection | ||||
| import java.util.concurrent.Executors | ||||
|  | ||||
| class RarContentProvider : ContentProvider() { | ||||
|  | ||||
|     private val pool by lazy { Executors.newCachedThreadPool() } | ||||
|  | ||||
|     companion object { | ||||
|         const val PROVIDER = "${BuildConfig.APPLICATION_ID}.rar-provider" | ||||
|     } | ||||
|  | ||||
|     override fun onCreate(): Boolean { | ||||
|         return true | ||||
|     } | ||||
|  | ||||
|     override fun getType(uri: Uri): String? { | ||||
|         return URLConnection.guessContentTypeFromName(uri.toString()) | ||||
|     } | ||||
|  | ||||
|     override fun openAssetFile(uri: Uri, mode: String): AssetFileDescriptor? { | ||||
|         try { | ||||
|             val pipe = ParcelFileDescriptor.createPipe() | ||||
|             pool.execute { | ||||
|                 try { | ||||
|                     val (rar, file) = uri.toString() | ||||
|                             .substringAfter("content://$PROVIDER") | ||||
|                             .split("!-/", limit = 2) | ||||
|  | ||||
|                     Archive(File(rar)).use { archive -> | ||||
|                         val fileHeader = archive.fileHeaders.first { it.fileNameString == file } | ||||
|  | ||||
|                         ParcelFileDescriptor.AutoCloseOutputStream(pipe[1]).use { output -> | ||||
|                             archive.extractFile(fileHeader, output) | ||||
|                         } | ||||
|                     } | ||||
|                 } catch (e: Exception) { | ||||
|                     // Ignore | ||||
|                 } | ||||
|             } | ||||
|             return AssetFileDescriptor(pipe[0], 0, -1) | ||||
|         } catch (e: IOException) { | ||||
|             return null | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun query(p0: Uri?, p1: Array<out String>?, p2: String?, p3: Array<out String>?, p4: String?): Cursor? { | ||||
|         return null | ||||
|     } | ||||
|  | ||||
|     override fun insert(p0: Uri?, p1: ContentValues?): Uri { | ||||
|         throw UnsupportedOperationException("not implemented") | ||||
|     } | ||||
|  | ||||
|     override fun update(p0: Uri?, p1: ContentValues?, p2: String?, p3: Array<out String>?): Int { | ||||
|         throw UnsupportedOperationException("not implemented") | ||||
|     } | ||||
|  | ||||
|     override fun delete(p0: Uri?, p1: String?, p2: Array<out String>?): Int { | ||||
|         throw UnsupportedOperationException("not implemented") | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user