diff --git a/app/build.gradle b/app/build.gradle index 03e1f5109..daaa07686 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,14 +9,14 @@ def gitCommits = 'git rev-list --count HEAD'.execute([], rootDir).text.trim().to android { compileSdkVersion 30 - buildToolsVersion '30.0.2' + buildToolsVersion '30.0.3' defaultConfig { applicationId 'org.koitharu.kotatsu' minSdkVersion 21 targetSdkVersion 30 versionCode gitCommits - versionName '1.0-b1' + versionName '1.0-b2' kapt { arguments { @@ -79,7 +79,7 @@ dependencies { implementation 'androidx.recyclerview:recyclerview:1.2.0-beta01' implementation 'androidx.viewpager2:viewpager2:1.1.0-alpha01' implementation 'androidx.preference:preference-ktx:1.1.1' - implementation 'androidx.work:work-runtime-ktx:2.4.0' + implementation 'androidx.work:work-runtime-ktx:2.5.0-beta02' implementation 'com.google.android.material:material:1.3.0-beta01' //noinspection LifecycleAnnotationProcessorWithJava8 kapt 'androidx.lifecycle:lifecycle-compiler:2.3.0-rc01' diff --git a/app/src/main/java/org/koitharu/kotatsu/core/model/MangaPage.kt b/app/src/main/java/org/koitharu/kotatsu/core/model/MangaPage.kt index 8d4a9f7e5..a85dc14bc 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/model/MangaPage.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/model/MangaPage.kt @@ -7,6 +7,7 @@ import kotlinx.parcelize.Parcelize data class MangaPage( val id: Long, val url: String, + val referer: String, val preview: String? = null, val source: MangaSource ) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt b/app/src/main/java/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt index 9d6a321cd..7ee4107d6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.core.network import okhttp3.Interceptor import okhttp3.Response +import okhttp3.internal.closeQuietly import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException import java.net.HttpURLConnection.HTTP_FORBIDDEN import java.net.HttpURLConnection.HTTP_UNAVAILABLE @@ -12,6 +13,7 @@ class CloudFlareInterceptor : Interceptor { val response = chain.proceed(chain.request()) if (response.code == HTTP_FORBIDDEN || response.code == HTTP_UNAVAILABLE) { if (response.header(HEADER_SERVER)?.startsWith(SERVER_CLOUDFLARE) == true) { + response.closeQuietly() throw CloudFlareProtectedException(chain.request().url.toString()) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt b/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt index 626c06c60..37d15b230 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/network/UserAgentInterceptor.kt @@ -9,12 +9,14 @@ class UserAgentInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain) = chain.proceed( chain.request().newBuilder() - .header("User-Agent", userAgent) + .header(HEADER_USER_AGENT, userAgent) .build() ) companion object { + private const val HEADER_USER_AGENT = "User-Agent" + val userAgent get() = "Kotatsu/%s (Android %s; %s; %s %s; %s)".format( BuildConfig.VERSION_NAME, diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt index 54fcb38d5..83c1f2de1 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt @@ -112,6 +112,7 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe MangaPage( id = url.longHashCode(), url = url, + referer = chapter.url, source = source ) } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/DesuMeRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/DesuMeRepository.kt index aefc070d0..47284b334 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/DesuMeRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/DesuMeRepository.kt @@ -103,6 +103,7 @@ class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor return json.getJSONObject("pages").getJSONArray("list").map { MangaPage( id = it.getLong("id"), + referer = chapter.url, source = chapter.source, url = it.getString("img") ) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt index 53d14a8ec..0a8d02522 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt @@ -154,6 +154,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) : MangaPage( id = url.longHashCode(), url = url, + referer = chapter.url, source = source ) } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt index 58bd3a19b..21d041768 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt @@ -168,8 +168,9 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) : val pageUrl = "$domain$url${x.getString("u")}" MangaPage( id = pageUrl.longHashCode(), - source = source, - url = pageUrl + url = pageUrl, + referer = chapter.url, + source = source ) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt index d51f38564..7ad9d98ee 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt @@ -134,6 +134,7 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) : MangaPage( id = href.longHashCode(), url = href, + referer = chapter.url, source = MangaSource.MANGATOWN ) } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt index 6e9a44eff..913f08029 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt @@ -146,6 +146,7 @@ class MangareadRepository( MangaPage( id = url.longHashCode(), url = url, + referer = chapter.url, source = MangaSource.MANGAREAD ) } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NudeMoonRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NudeMoonRepository.kt index 9e45028c0..c2caa5b63 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NudeMoonRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NudeMoonRepository.kt @@ -109,6 +109,7 @@ class NudeMoonRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposit MangaPage( id = url.longHashCode(), url = url, + referer = chapter.url, preview = a.selectFirst("img")?.absUrl("src"), source = source ) diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt index a1356d821..098df9d17 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt @@ -51,6 +51,7 @@ class DetailsFragment : BaseFragment(), View.OnClickList private fun onMangaUpdated(manga: Manga) { with(binding) { imageViewCover.newImageRequest(manga.largeCoverUrl ?: manga.coverUrl) + .referer(manga.url) .fallback(R.drawable.ic_placeholder) .placeholderMemoryCacheKey(CoilUtils.metadata(imageViewCover)?.memoryCacheKey) .lifecycle(viewLifecycleOwner) diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt index f096c8892..783dc47ed 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt @@ -13,6 +13,7 @@ import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.MangaGridModel import org.koitharu.kotatsu.utils.ext.enqueueWith import org.koitharu.kotatsu.utils.ext.newImageRequest +import org.koitharu.kotatsu.utils.ext.referer fun mangaGridItemAD( coil: ImageLoader, @@ -35,6 +36,7 @@ fun mangaGridItemAD( binding.textViewTitle.text = item.title imageRequest?.dispose() imageRequest = binding.imageViewCover.newImageRequest(item.coverUrl) + .referer(item.manga.url) .placeholder(R.drawable.ic_placeholder) .fallback(R.drawable.ic_placeholder) .error(R.drawable.ic_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt index c318bdfca..906c50fdc 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt @@ -13,6 +13,7 @@ import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.MangaListDetailedModel import org.koitharu.kotatsu.utils.ext.enqueueWith import org.koitharu.kotatsu.utils.ext.newImageRequest +import org.koitharu.kotatsu.utils.ext.referer import org.koitharu.kotatsu.utils.ext.textAndVisible fun mangaListDetailedItemAD( @@ -37,6 +38,7 @@ fun mangaListDetailedItemAD( binding.textViewTitle.text = item.title binding.textViewSubtitle.textAndVisible = item.subtitle imageRequest = binding.imageViewCover.newImageRequest(item.coverUrl) + .referer(item.manga.url) .placeholder(R.drawable.ic_placeholder) .fallback(R.drawable.ic_placeholder) .error(R.drawable.ic_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt index 021ce7e44..510cea162 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt @@ -13,6 +13,7 @@ import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.MangaListModel import org.koitharu.kotatsu.utils.ext.enqueueWith import org.koitharu.kotatsu.utils.ext.newImageRequest +import org.koitharu.kotatsu.utils.ext.referer import org.koitharu.kotatsu.utils.ext.textAndVisible fun mangaListItemAD( @@ -37,6 +38,7 @@ fun mangaListItemAD( binding.textViewTitle.text = item.title binding.textViewSubtitle.textAndVisible = item.subtitle imageRequest = binding.imageViewCover.newImageRequest(item.coverUrl) + .referer(item.manga.url) .placeholder(R.drawable.ic_placeholder) .fallback(R.drawable.ic_placeholder) .error(R.drawable.ic_placeholder) diff --git a/app/src/main/java/org/koitharu/kotatsu/local/data/PagesCache.kt b/app/src/main/java/org/koitharu/kotatsu/local/data/PagesCache.kt index 8630ed688..66d1b2f4d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/local/data/PagesCache.kt +++ b/app/src/main/java/org/koitharu/kotatsu/local/data/PagesCache.kt @@ -7,6 +7,7 @@ import org.koitharu.kotatsu.utils.ext.longHashCode import org.koitharu.kotatsu.utils.ext.sub import org.koitharu.kotatsu.utils.ext.takeIfReadable import java.io.File +import java.io.InputStream import java.io.OutputStream class PagesCache(context: Context) { @@ -19,6 +20,7 @@ class PagesCache(context: Context) { return lruCache.get(url)?.takeIfReadable() } + @Deprecated("Useless lambda") fun put(url: String, writer: (OutputStream) -> Unit): File { val file = cacheDir.sub(url.longHashCode().toString()) file.outputStream().use(writer) @@ -26,4 +28,14 @@ class PagesCache(context: Context) { file.delete() return res } + + fun put(url: String, inputStream: InputStream): File { + val file = cacheDir.sub(url.longHashCode().toString()) + file.outputStream().use { out -> + inputStream.copyTo(out) + } + val res = lruCache.put(url, file) + file.delete() + return res + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/local/domain/LocalMangaRepository.kt b/app/src/main/java/org/koitharu/kotatsu/local/domain/LocalMangaRepository.kt index 9fe679199..34dd84e87 100644 --- a/app/src/main/java/org/koitharu/kotatsu/local/domain/LocalMangaRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/local/domain/LocalMangaRepository.kt @@ -71,6 +71,7 @@ class LocalMangaRepository(private val context: Context) : MangaRepository { MangaPage( id = entryUri.longHashCode(), url = entryUri, + referer = chapter.url, source = MangaSource.LOCAL ) } diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/PageLoader.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/PageLoader.kt index 728e32169..b918e66ec 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/PageLoader.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/PageLoader.kt @@ -24,43 +24,41 @@ class PageLoader( private val tasks = ArrayMap>() private val convertLock = Mutex() - suspend fun loadFile(url: String, force: Boolean): File { + suspend fun loadFile(url: String, referer: String, force: Boolean): File { if (!force) { cache[url]?.let { return it } } val task = tasks[url]?.takeUnless { it.isCancelled || (force && it.isCompleted) } - return (task ?: loadAsync(url).also { tasks[url] = it }).await() + return (task ?: loadAsync(url, referer).also { tasks[url] = it }).await() } - private fun loadAsync(url: String) = async(Dispatchers.IO) { + private fun loadAsync(url: String, referer: String) = async(Dispatchers.IO) { val uri = Uri.parse(url) if (uri.scheme == "cbz") { val zip = ZipFile(uri.schemeSpecificPart) val entry = zip.getEntry(uri.fragment) zip.getInputStream(entry).use { - cache.put(url) { out -> - it.copyTo(out) - } + cache.put(url, it) } } else { val request = Request.Builder() .url(url) .get() .header("Accept", "image/webp,image/png;q=0.9,image/jpeg,*/*;q=0.8") + .header("Referer", referer) .cacheControl(CacheUtils.CONTROL_DISABLED) .build() okHttp.newCall(request).await().use { response -> - val body = response.body check(response.isSuccessful) { "Invalid response: ${response.code} ${response.message}" } - checkNotNull(body) { + val body = checkNotNull(response.body) { "Null response" } - cache.put(url) { out -> - body.byteStream().use { it.copyTo(out) } + body.byteStream().use { + cache.put(url, it) } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt index 48a09b291..7bec0652d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt @@ -91,7 +91,7 @@ class PageHolderDelegate( val file = withContext(Dispatchers.IO) { val pageUrl = data.source.repository.getPageFullUrl(data) check(pageUrl.isNotEmpty()) { "Cannot obtain full image url" } - loader.loadFile(pageUrl, force) + loader.loadFile(pageUrl, data.referer, force) } this@PageHolderDelegate.file = file state = State.LOADED diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/ReaderPage.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/ReaderPage.kt index 24b24e41d..7ff4106cb 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/ReaderPage.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/ReaderPage.kt @@ -9,6 +9,7 @@ import org.koitharu.kotatsu.core.model.MangaSource data class ReaderPage( val id: Long, val url: String, + val referer: String, val preview: String?, val chapterId: Long, val index: Int, @@ -18,6 +19,7 @@ data class ReaderPage( fun toMangaPage() = MangaPage( id = id, url = url, + referer = referer, preview = preview, source = source ) @@ -27,6 +29,7 @@ data class ReaderPage( fun from(page: MangaPage, index: Int, chapterId: Long) = ReaderPage( id = page.id, url = page.url, + referer = page.referer, preview = page.preview, chapterId = chapterId, index = index, diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt index 13239be72..79f966f60 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt @@ -28,4 +28,7 @@ fun ImageResult.toBitmapOrNull() = when (this) { null } is ErrorResult -> null -} \ No newline at end of file +} + +@Suppress("NOTHING_TO_INLINE") +inline fun ImageRequest.Builder.referer(referer: String) = this.setHeader("Referer", referer) \ No newline at end of file diff --git a/app/src/test/java/org/koitharu/kotatsu/parsers/RemoteRepositoryTest.kt b/app/src/test/java/org/koitharu/kotatsu/parsers/RemoteRepositoryTest.kt index 36328223b..179bbe28e 100644 --- a/app/src/test/java/org/koitharu/kotatsu/parsers/RemoteRepositoryTest.kt +++ b/app/src/test/java/org/koitharu/kotatsu/parsers/RemoteRepositoryTest.kt @@ -24,7 +24,7 @@ class RemoteRepositoryTest(source: MangaSource) : KoinTest { private val repo = try { source.cls.getDeclaredConstructor(MangaLoaderContext::class.java) - .newInstance(get()) + .newInstance(get()) } catch (e: NoSuchMethodException) { source.cls.newInstance() }