This commit is contained in:
len 2016-11-24 21:42:01 +01:00
parent 87281d34c1
commit 93e244b4c4
6 changed files with 86 additions and 115 deletions

View File

@ -6,7 +6,7 @@ import com.google.gson.Gson
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.jakewharton.disklrucache.DiskLruCache import com.jakewharton.disklrucache.DiskLruCache
import eu.kanade.tachiyomi.data.source.model.Page import eu.kanade.tachiyomi.data.source.model.Page
import eu.kanade.tachiyomi.util.DiskUtils import eu.kanade.tachiyomi.util.DiskUtil
import eu.kanade.tachiyomi.util.saveTo import eu.kanade.tachiyomi.util.saveTo
import okhttp3.Response import okhttp3.Response
import okio.Okio import okio.Okio
@ -70,7 +70,7 @@ class ChapterCache(private val context: Context) {
* @return real size of directory. * @return real size of directory.
*/ */
private val realSize: Long private val realSize: Long
get() = DiskUtils.getDirectorySize(cacheDir) get() = DiskUtil.getDirectorySize(cacheDir)
/** /**
* Returns real size of directory in human readable format. * Returns real size of directory in human readable format.
@ -107,7 +107,7 @@ class ChapterCache(private val context: Context) {
fun getPageListFromCache(chapterUrl: String): Observable<List<Page>> { fun getPageListFromCache(chapterUrl: String): Observable<List<Page>> {
return Observable.fromCallable<List<Page>> { return Observable.fromCallable<List<Page>> {
// Get the key for the chapter. // Get the key for the chapter.
val key = DiskUtils.hashKeyForDisk(chapterUrl) val key = DiskUtil.hashKeyForDisk(chapterUrl)
// Convert JSON string to list of objects. Throws an exception if snapshot is null // Convert JSON string to list of objects. Throws an exception if snapshot is null
diskCache.get(key).use { diskCache.get(key).use {
@ -130,7 +130,7 @@ class ChapterCache(private val context: Context) {
try { try {
// Get editor from md5 key. // Get editor from md5 key.
val key = DiskUtils.hashKeyForDisk(chapterUrl) val key = DiskUtil.hashKeyForDisk(chapterUrl)
editor = diskCache.edit(key) ?: return editor = diskCache.edit(key) ?: return
// Write chapter urls to cache. // Write chapter urls to cache.
@ -157,7 +157,7 @@ class ChapterCache(private val context: Context) {
*/ */
fun isImageInCache(imageUrl: String): Boolean { fun isImageInCache(imageUrl: String): Boolean {
try { try {
return diskCache.get(DiskUtils.hashKeyForDisk(imageUrl)) != null return diskCache.get(DiskUtil.hashKeyForDisk(imageUrl)) != null
} catch (e: IOException) { } catch (e: IOException) {
return false return false
} }
@ -171,7 +171,7 @@ class ChapterCache(private val context: Context) {
fun getImagePath(imageUrl: String): File? { fun getImagePath(imageUrl: String): File? {
try { try {
// Get file from md5 key. // Get file from md5 key.
val imageName = DiskUtils.hashKeyForDisk(imageUrl) + ".0" val imageName = DiskUtil.hashKeyForDisk(imageUrl) + ".0"
return File(diskCache.directory, imageName) return File(diskCache.directory, imageName)
} catch (e: IOException) { } catch (e: IOException) {
return null return null
@ -191,7 +191,7 @@ class ChapterCache(private val context: Context) {
try { try {
// Get editor from md5 key. // Get editor from md5 key.
val key = DiskUtils.hashKeyForDisk(imageUrl) val key = DiskUtil.hashKeyForDisk(imageUrl)
editor = diskCache.edit(key) ?: throw IOException("Unable to edit key") editor = diskCache.edit(key) ?: throw IOException("Unable to edit key")
// Get OutputStream and write image with Okio. // Get OutputStream and write image with Okio.

View File

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.data.cache package eu.kanade.tachiyomi.data.cache
import android.content.Context import android.content.Context
import eu.kanade.tachiyomi.util.DiskUtils import eu.kanade.tachiyomi.util.DiskUtil
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
@ -29,7 +29,7 @@ class CoverCache(private val context: Context) {
* @return cover image. * @return cover image.
*/ */
fun getCoverFile(thumbnailUrl: String): File { fun getCoverFile(thumbnailUrl: String): File {
return File(cacheDir, DiskUtils.hashKeyForDisk(thumbnailUrl)) return File(cacheDir, DiskUtil.hashKeyForDisk(thumbnailUrl))
} }
/** /**

View File

@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.source.Source import eu.kanade.tachiyomi.data.source.Source
import eu.kanade.tachiyomi.util.DiskUtil
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
/** /**
@ -82,7 +83,7 @@ class DownloadProvider(private val context: Context) {
* @param manga the manga to query. * @param manga the manga to query.
*/ */
fun getMangaDirName(manga: Manga): String { fun getMangaDirName(manga: Manga): String {
return buildValidFatFilename(manga.title.trim('.', ' ')) return DiskUtil.buildValidFatFilename(manga.title)
} }
/** /**
@ -91,40 +92,7 @@ class DownloadProvider(private val context: Context) {
* @param chapter the chapter to query. * @param chapter the chapter to query.
*/ */
fun getChapterDirName(chapter: Chapter): String { fun getChapterDirName(chapter: Chapter): String {
return buildValidFatFilename(chapter.name.trim('.', ' ')) return DiskUtil.buildValidFatFilename(chapter.name)
} }
/**
* Mutate the given filename to make it valid for a FAT filesystem,
* replacing any invalid characters with "_".
*/
private fun buildValidFatFilename(name: String): String {
if (name.isNullOrEmpty()) {
return "(invalid)"
}
val res = StringBuilder(name.length)
name.forEach { c ->
if (isValidFatFilenameChar(c)) {
res.append(c)
} else {
res.append('_')
}
}
// Even though vfat allows 255 UCS-2 chars, we might eventually write to
// ext4 through a FUSE layer, so use that limit minus 5 reserved characters.
return res.toString().take(250)
}
/**
* Returns true if the given character is a valid filename character, false otherwise.
*/
private fun isValidFatFilenameChar(c: Char): Boolean {
if (0x00.toChar() <= c && c <= 0x1f.toChar()) {
return false
}
when (c) {
'"', '*', '/', ':', '<', '>', '?', '\\', '|', 0x7f.toChar() -> return false
else -> return true
}
}
} }

View File

@ -20,6 +20,7 @@ import eu.kanade.tachiyomi.data.source.model.Page
import eu.kanade.tachiyomi.data.source.online.OnlineSource import eu.kanade.tachiyomi.data.source.online.OnlineSource
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.reader.notification.ImageNotifier import eu.kanade.tachiyomi.ui.reader.notification.ImageNotifier
import eu.kanade.tachiyomi.util.DiskUtil
import eu.kanade.tachiyomi.util.RetryWithDelay import eu.kanade.tachiyomi.util.RetryWithDelay
import eu.kanade.tachiyomi.util.SharedData import eu.kanade.tachiyomi.util.SharedData
import eu.kanade.tachiyomi.util.toast import eu.kanade.tachiyomi.util.toast
@ -577,8 +578,9 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
val ext = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime) ?: "jpg" val ext = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime) ?: "jpg"
// Destination file. // Destination file.
val destFile = File(destDir, manga.title + " - " + chapter.name +
" - " + (page.index + 1) + ".$ext") val filename = "${manga.title} - ${chapter.name} - ${page.index + 1}.$ext"
val destFile = File(destDir, DiskUtil.buildValidFatFilename(filename))
context.contentResolver.openInputStream(page.uri).use { input -> context.contentResolver.openInputStream(page.uri).use { input ->
destFile.outputStream().use { output -> destFile.outputStream().use { output ->

View File

@ -0,0 +1,70 @@
package eu.kanade.tachiyomi.util
import java.io.File
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
object DiskUtil {
fun hashKeyForDisk(key: String): String {
return try {
val bytes = MessageDigest.getInstance("MD5").digest(key.toByteArray())
val sb = StringBuilder()
bytes.forEach { byte ->
sb.append(Integer.toHexString(byte.toInt() and 0xFF or 0x100).substring(1, 3))
}
sb.toString()
} catch (e: NoSuchAlgorithmException) {
key.hashCode().toString()
}
}
fun getDirectorySize(f: File): Long {
var size: Long = 0
if (f.isDirectory) {
for (file in f.listFiles()) {
size += getDirectorySize(file)
}
} else {
size = f.length()
}
return size
}
/**
* Mutate the given filename to make it valid for a FAT filesystem,
* replacing any invalid characters with "_". This method doesn't allow private files (starting
* with a dot), but you can manually add it later.
*/
fun buildValidFatFilename(origName: String): String {
val name = origName.trim('.', ' ')
if (name.isNullOrEmpty()) {
return "(invalid)"
}
val sb = StringBuilder(name.length)
name.forEach { c ->
if (isValidFatFilenameChar(c)) {
sb.append(c)
} else {
sb.append('_')
}
}
// Even though vfat allows 255 UCS-2 chars, we might eventually write to
// ext4 through a FUSE layer, so use that limit minus 5 reserved characters.
return sb.toString().take(250)
}
/**
* Returns true if the given character is a valid filename character, false otherwise.
*/
private fun isValidFatFilenameChar(c: Char): Boolean {
if (0x00.toChar() <= c && c <= 0x1f.toChar()) {
return false
}
return when (c) {
'"', '*', '/', ':', '<', '>', '?', '\\', '|', 0x7f.toChar() -> false
else -> true
}
}
}

View File

@ -1,69 +0,0 @@
package eu.kanade.tachiyomi.util;
import java.io.File;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public final class DiskUtils {
private DiskUtils() {
throw new AssertionError();
}
public static String hashKeyForDisk(String key) {
String cacheKey;
try {
final MessageDigest mDigest = MessageDigest.getInstance("MD5");
mDigest.update(key.getBytes());
cacheKey = bytesToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
cacheKey = String.valueOf(key.hashCode());
}
return cacheKey;
}
private static String bytesToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(0xFF & bytes[i]);
if (hex.length() == 1) {
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}
public static void deleteFiles(File inputFile) {
if (inputFile.isDirectory()) {
for (File childFile : inputFile.listFiles()) {
deleteFiles(childFile);
}
}
//noinspection ResultOfMethodCallIgnored
inputFile.delete();
}
public static synchronized void createDirectory(File directory) throws IOException {
if (!directory.exists() && !directory.mkdirs()) {
throw new IOException("Failed creating directory");
}
}
public static long getDirectorySize(File f) {
long size = 0;
if (f.isDirectory()) {
for (File file : f.listFiles()) {
size += getDirectorySize(file);
}
} else {
size=f.length();
}
return size;
}
}