Rar/cbr support
This commit is contained in:
parent
aeef8c02d8
commit
c8e3375248
4 changed files with 102 additions and 3 deletions
|
@ -99,6 +99,7 @@ dependencies {
|
||||||
|
|
||||||
// Modified dependencies
|
// Modified dependencies
|
||||||
compile 'com.github.inorichi:subsampling-scale-image-view:44aa442'
|
compile 'com.github.inorichi:subsampling-scale-image-view:44aa442'
|
||||||
|
compile 'com.github.inorichi:junrar-android:634c1f5'
|
||||||
|
|
||||||
// Android support library
|
// Android support library
|
||||||
final support_library_version = '25.1.1'
|
final support_library_version = '25.1.1'
|
||||||
|
|
|
@ -81,6 +81,11 @@
|
||||||
android:authorities="${applicationId}.zip-provider"
|
android:authorities="${applicationId}.zip-provider"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="eu.kanade.tachiyomi.util.RarContentProvider"
|
||||||
|
android:authorities="${applicationId}.rar-provider"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".data.notification.NotificationReceiver"
|
android:name=".data.notification.NotificationReceiver"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
|
@ -6,7 +6,10 @@ import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.source.model.*
|
import eu.kanade.tachiyomi.source.model.*
|
||||||
import eu.kanade.tachiyomi.util.ChapterRecognition
|
import eu.kanade.tachiyomi.util.ChapterRecognition
|
||||||
import eu.kanade.tachiyomi.util.DiskUtil
|
import eu.kanade.tachiyomi.util.DiskUtil
|
||||||
|
import eu.kanade.tachiyomi.util.RarContentProvider
|
||||||
import eu.kanade.tachiyomi.util.ZipContentProvider
|
import eu.kanade.tachiyomi.util.ZipContentProvider
|
||||||
|
import junrar.Archive
|
||||||
|
import junrar.rarfile.FileHeader
|
||||||
import net.greypanther.natsort.CaseInsensitiveSimpleNaturalComparator
|
import net.greypanther.natsort.CaseInsensitiveSimpleNaturalComparator
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
|
@ -169,7 +172,9 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSupportedFormat(extension: String): Boolean {
|
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 {
|
private fun getLoader(file: File): Loader {
|
||||||
|
@ -180,6 +185,8 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
||||||
ZipLoader(file)
|
ZipLoader(file)
|
||||||
} else if (extension.equals("epub", true)) {
|
} else if (extension.equals("epub", true)) {
|
||||||
EpubLoader(file)
|
EpubLoader(file)
|
||||||
|
} else if (extension.equals("rar", true) || extension.equals("cbr", true)) {
|
||||||
|
RarLoader(file)
|
||||||
} else {
|
} else {
|
||||||
throw Exception("Invalid chapter format")
|
throw Exception("Invalid chapter format")
|
||||||
}
|
}
|
||||||
|
@ -207,8 +214,8 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
||||||
class ZipLoader(val file: File) : Loader {
|
class ZipLoader(val file: File) : Loader {
|
||||||
override fun load(): List<Page> {
|
override fun load(): List<Page> {
|
||||||
val comparator = CaseInsensitiveSimpleNaturalComparator.getInstance<String>()
|
val comparator = CaseInsensitiveSimpleNaturalComparator.getInstance<String>()
|
||||||
ZipFile(file).use { zip ->
|
return ZipFile(file).use { zip ->
|
||||||
return zip.entries().toList()
|
zip.entries().toList()
|
||||||
.filter { !it.isDirectory && DiskUtil.isImage(it.name, { zip.getInputStream(it) }) }
|
.filter { !it.isDirectory && DiskUtil.isImage(it.name, { zip.getInputStream(it) }) }
|
||||||
.sortedWith(Comparator<ZipEntry> { f1, f2 -> comparator.compare(f1.name, f2.name) })
|
.sortedWith(Comparator<ZipEntry> { f1, f2 -> comparator.compare(f1.name, f2.name) })
|
||||||
.map { Uri.parse("content://${ZipContentProvider.PROVIDER}${file.absolutePath}!/${it.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 {
|
class EpubLoader(val file: File) : Loader {
|
||||||
|
|
||||||
override fun load(): List<Page> {
|
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 a new issue