浏览代码

Local chapter url relative. Other minor changes

len 8 年之前
父节点
当前提交
e8912c5dc9

+ 1 - 1
app/src/main/AndroidManifest.xml

@@ -79,7 +79,7 @@
         <provider
             android:name="eu.kanade.tachiyomi.util.ZipContentProvider"
             android:authorities="${applicationId}.zip-provider"
-            android:exported="false"></provider>
+            android:exported="false" />
 
         <receiver
             android:name=".data.notification.NotificationReceiver"

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/data/database/models/MangaImpl.kt

@@ -4,7 +4,7 @@ class MangaImpl : Manga {
 
     override var id: Long? = null
 
-    override var source: Long = 0
+    override var source: Long = -1
 
     override lateinit var url: String
 

+ 29 - 0
app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaFileFetcher.kt

@@ -0,0 +1,29 @@
+package eu.kanade.tachiyomi.data.glide
+
+import com.bumptech.glide.Priority
+import com.bumptech.glide.load.data.DataFetcher
+import eu.kanade.tachiyomi.data.database.models.Manga
+import java.io.File
+import java.io.InputStream
+
+class MangaFileFetcher(private val fetcher: DataFetcher<InputStream>,
+                       private val file: File,
+                       private val manga: Manga) : DataFetcher<InputStream> {
+
+
+    override fun loadData(priority: Priority?): InputStream? {
+        return fetcher.loadData(priority)
+    }
+
+    override fun getId(): String {
+        return manga.thumbnail_url + file.lastModified()
+    }
+
+    override fun cancel() {
+        fetcher.cancel()
+    }
+
+    override fun cleanup() {
+        fetcher.cleanup()
+    }
+}

+ 27 - 30
app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaModelLoader.kt

@@ -1,10 +1,8 @@
 package eu.kanade.tachiyomi.data.glide
 
 import android.content.Context
-import android.net.Uri
 import android.util.LruCache
 import com.bumptech.glide.Glide
-import com.bumptech.glide.Priority
 import com.bumptech.glide.load.data.DataFetcher
 import com.bumptech.glide.load.model.*
 import com.bumptech.glide.load.model.stream.StreamModelLoader
@@ -18,7 +16,7 @@ import java.io.InputStream
 
 /**
  * A class for loading a cover associated with a [Manga] that can be present in our own cache.
- * Coupled with [MangaDataFetcher], this class allows to implement the following flow:
+ * Coupled with [MangaUrlFetcher], this class allows to implement the following flow:
  *
  * - Check in RAM LRU.
  * - Check in disk LRU.
@@ -32,23 +30,23 @@ class MangaModelLoader(context: Context) : StreamModelLoader<Manga> {
     /**
      * Cover cache where persistent covers are stored.
      */
-    val coverCache: CoverCache by injectLazy()
+    private val coverCache: CoverCache by injectLazy()
 
     /**
      * Source manager.
      */
-    val sourceManager: SourceManager by injectLazy()
+    private val sourceManager: SourceManager by injectLazy()
 
     /**
      * Base network loader.
      */
-    private val baseLoader = Glide.buildModelLoader(GlideUrl::class.java,
+    private val baseUrlLoader = Glide.buildModelLoader(GlideUrl::class.java,
             InputStream::class.java, context)
 
     /**
      * Base file loader.
      */
-    private val baseFileLoader = Glide.buildModelLoader(Uri::class.java,
+    private val baseFileLoader = Glide.buildModelLoader(File::class.java,
             InputStream::class.java, context)
 
     /**
@@ -74,7 +72,7 @@ class MangaModelLoader(context: Context) : StreamModelLoader<Manga> {
     }
 
     /**
-     * Returns a [MangaDataFetcher] for the given manga or null if the url is empty.
+     * Returns a fetcher for the given manga or null if the url is empty.
      *
      * @param manga the model.
      * @param width the width of the view where the resource will be loaded.
@@ -86,34 +84,33 @@ class MangaModelLoader(context: Context) : StreamModelLoader<Manga> {
 
         // Check thumbnail is not null or empty
         val url = manga.thumbnail_url
-        if (url.isNullOrEmpty()) {
+        if (url == null || url.isEmpty()) {
             return null
         }
 
-        if (url!!.startsWith("file://")) {
-            val cover = File(url.substring(7))
-            val id = url + File.separator + cover.lastModified()
-            val rf = baseFileLoader.getResourceFetcher(Uri.fromFile(cover), width, height)
-            return object : DataFetcher<InputStream> {
-                override fun cleanup() = rf.cleanup()
-                override fun loadData(priority: Priority?): InputStream = rf.loadData(priority)
-                override fun cancel() = rf.cancel()
-                override fun getId() = id
-            }
-        }
+        if (url.startsWith("http")) {
+            // Obtain the request url and the file for this url from the LRU cache, or calculate it
+            // and add them to the cache.
+            val (glideUrl, file) = lruCache.get(url) ?:
+                    Pair(GlideUrl(url, getHeaders(manga)), coverCache.getCoverFile(url)).apply {
+                        lruCache.put(url, this)
+                    }
+
+            // Get the resource fetcher for this request url.
+            val networkFetcher = baseUrlLoader.getResourceFetcher(glideUrl, width, height)
 
-        // Obtain the request url and the file for this url from the LRU cache, or calculate it
-        // and add them to the cache.
-        val (glideUrl, file) = lruCache.get(url) ?:
-            Pair(GlideUrl(url, getHeaders(manga)), coverCache.getCoverFile(url!!)).apply {
-                lruCache.put(url, this)
-            }
+            // Return an instance of the fetcher providing the needed elements.
+            return MangaUrlFetcher(networkFetcher, file, manga)
+        } else {
+            // Get the file from the url, removing the scheme if present.
+            val file = File(url.substringAfter("file://"))
 
-        // Get the network fetcher for this request url.
-        val networkFetcher = baseLoader.getResourceFetcher(glideUrl, width, height)
+            // Get the resource fetcher for the given file.
+            val fileFetcher = baseFileLoader.getResourceFetcher(file, width, height)
 
-        // Return an instance of our fetcher providing the needed elements.
-        return MangaDataFetcher(networkFetcher, file, manga)
+            // Return an instance of the fetcher providing the needed elements.
+            return MangaFileFetcher(fileFetcher, file, manga)
+        }
     }
 
     /**

+ 3 - 3
app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaDataFetcher.kt → app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaUrlFetcher.kt

@@ -18,9 +18,9 @@ import java.io.InputStream
  * @param file the file where this cover should be. It may exists or not.
  * @param manga the manga of the cover to load.
  */
-class MangaDataFetcher(private val networkFetcher: DataFetcher<InputStream>,
-                       private val file: File,
-                       private val manga: Manga)
+class MangaUrlFetcher(private val networkFetcher: DataFetcher<InputStream>,
+                      private val file: File,
+                      private val manga: Manga)
 : DataFetcher<InputStream> {
 
     @Throws(Exception::class)

+ 31 - 22
app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt

@@ -20,7 +20,6 @@ import java.util.zip.ZipFile
 
 class LocalSource(private val context: Context) : CatalogueSource {
     companion object {
-        private val FILE_PROTOCOL = "file://"
         private val COVER_NAME = "cover.jpg"
         private val POPULAR_FILTERS = FilterList(OrderBy())
         private val LATEST_FILTERS = FilterList(OrderBy().apply { state = Filter.Sort.Selection(1, false) })
@@ -46,8 +45,8 @@ class LocalSource(private val context: Context) : CatalogueSource {
         }
 
         private fun getBaseDirectories(context: Context): List<File> {
-            val c = File.separator + context.getString(R.string.app_name) + File.separator + "local"
-            return DiskUtil.getExternalStorages(context).map { File(it.absolutePath + c) }
+            val c = context.getString(R.string.app_name) + File.separator + "local"
+            return DiskUtil.getExternalStorages(context).map { File(it.absolutePath, c) }
         }
     }
 
@@ -67,7 +66,7 @@ class LocalSource(private val context: Context) : CatalogueSource {
                 .filter { it.isDirectory || isSupportedFormat(it.extension) }
                 .map { chapterFile ->
                     SChapter.create().apply {
-                        url = chapterFile.absolutePath
+                        url = "${manga.url}/${chapterFile.name}"
                         val chapName = if (chapterFile.isDirectory) {
                             chapterFile.name
                         } else {
@@ -79,27 +78,37 @@ class LocalSource(private val context: Context) : CatalogueSource {
                         ChapterRecognition.parseChapterNumber(this, manga)
                     }
                 }
+                .sortedByDescending { it.chapter_number }
 
-        return Observable.just(chapters.sortedByDescending { it.chapter_number })
+        return Observable.just(chapters)
     }
 
     override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
-        val chapFile = File(chapter.url)
-        if (chapFile.isDirectory) {
-            return Observable.just(chapFile.listFiles()
-                    .filter { !it.isDirectory && DiskUtil.isImage(it.name, { FileInputStream(it) }) }
-                    .sortedWith(Comparator<File> { t1, t2 -> CaseInsensitiveSimpleNaturalComparator.getInstance<String>().compare(t1.name, t2.name) })
-                    .mapIndexed { i, v -> Page(i, FILE_PROTOCOL + v.absolutePath, FILE_PROTOCOL + v.absolutePath, Uri.fromFile(v)).apply { status = Page.READY } })
-        } else {
-            val zip = ZipFile(chapFile)
-            return Observable.just(ZipFile(chapFile).entries().toList()
-                    .filter { !it.isDirectory && DiskUtil.isImage(it.name, { zip.getInputStream(it) }) }
-                    .sortedWith(Comparator<ZipEntry> { t1, t2 -> CaseInsensitiveSimpleNaturalComparator.getInstance<String>().compare(t1.name, t2.name) })
-                    .mapIndexed { i, v ->
-                        val path = "content://${ZipContentProvider.PROVIDER}${chapFile.absolutePath}!/${v.name}"
-                        Page(i, path, path, Uri.parse(path)).apply { status = Page.READY }
-                    })
+        val baseDirs = getBaseDirectories(context)
+
+        for (dir in baseDirs) {
+            val chapFile = File(dir, chapter.url)
+            if (!chapFile.exists()) continue
+
+            val comparator = CaseInsensitiveSimpleNaturalComparator.getInstance<String>()
+
+            val pageList = if (chapFile.isDirectory) {
+                chapFile.listFiles()
+                        .filter { !it.isDirectory && DiskUtil.isImage(it.name, { FileInputStream(it) }) }
+                        .sortedWith(Comparator<File> { f1, f2 -> comparator.compare(f1.name, f2.name) })
+                        .map { Uri.fromFile(it) }
+            } else {
+                val zip = ZipFile(chapFile)
+                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}${chapFile.absolutePath}!/${it.name}") }
+            }.mapIndexed { i, uri -> Page(i, uri = uri).apply { status = Page.READY } }
+
+            return Observable.just(pageList)
         }
+
+        return Observable.error(Exception("Chapter not found"))
     }
 
     override fun fetchPopularManga(page: Int) = fetchSearchManga(page, "", POPULAR_FILTERS)
@@ -138,7 +147,7 @@ class LocalSource(private val context: Context) : CatalogueSource {
                 for (dir in baseDirs) {
                     val cover = File("${dir.absolutePath}/$url", COVER_NAME)
                     if (cover.exists()) {
-                        thumbnail_url = FILE_PROTOCOL + cover.absolutePath
+                        thumbnail_url = cover.absolutePath
                         break
                     }
                 }
@@ -152,7 +161,7 @@ class LocalSource(private val context: Context) : CatalogueSource {
                             val input = context.contentResolver.openInputStream(Uri.parse(url))
                             try {
                                 val dest = updateCover(context, this, input)
-                                thumbnail_url = dest?.let { FILE_PROTOCOL + it.absolutePath }
+                                thumbnail_url = dest?.absolutePath
                             } catch (e: Exception) {
                                 Timber.e(e)
                             }

+ 1 - 3
app/src/main/java/eu/kanade/tachiyomi/util/ZipContentProvider.kt

@@ -7,7 +7,6 @@ import android.database.Cursor
 import android.net.Uri
 import android.os.ParcelFileDescriptor
 import eu.kanade.tachiyomi.BuildConfig
-import timber.log.Timber
 import java.io.IOException
 import java.net.URL
 import java.net.URLConnection
@@ -40,11 +39,10 @@ class ZipContentProvider : ContentProvider() {
                     input.use {
                         output.use {
                             input.copyTo(output)
-                            output.flush()
                         }
                     }
                 } catch (e: IOException) {
-                    Timber.e(e)
+                    // Ignore
                 }
             }
             return AssetFileDescriptor(pipe[0], 0, -1)