Hiyobi file structure changed

This commit is contained in:
Pupil
2020-02-22 11:02:58 +09:00
parent c0e7c87ca4
commit 4f0dbead79
13 changed files with 65 additions and 44 deletions

View File

@@ -20,7 +20,7 @@ android {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 29 targetSdkVersion 29
versionCode 42 versionCode 42
versionName "4.6-alpha7" versionName "4.7-alpha1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true

View File

@@ -1 +1 @@
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":42,"versionName":"4.6-alpha7","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] [{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":42,"versionName":"4.7-alpha1","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]

View File

@@ -90,7 +90,7 @@ class GalleryBlockAdapter(context: Context, private val galleries: List<GalleryB
if (visibility == View.GONE) { if (visibility == View.GONE) {
visibility = View.VISIBLE visibility = View.VISIBLE
max = reader.galleryInfo.size max = reader.galleryInfo.files.size
} }
if (progress == max) { if (progress == max) {
@@ -160,7 +160,7 @@ class GalleryBlockAdapter(context: Context, private val galleries: List<GalleryB
} ?: 0 } ?: 0
with(galleryblock_progressbar) { with(galleryblock_progressbar) {
max = reader.galleryInfo.size max = reader.galleryInfo.files.size
progress = count progress = count
visibility = View.VISIBLE visibility = View.VISIBLE

View File

@@ -52,7 +52,7 @@ class ReaderAdapter(private val context: Context,
//region Glide.RecyclerView //region Glide.RecyclerView
val sizeProvider = ListPreloader.PreloadSizeProvider<File> { _, _, position -> val sizeProvider = ListPreloader.PreloadSizeProvider<File> { _, _, position ->
Cache(context).getReaderOrNull(galleryID)?.galleryInfo?.getOrNull(position)?.let { Cache(context).getReaderOrNull(galleryID)?.galleryInfo?.files?.getOrNull(position)?.let {
arrayOf(it.width, it.height).toIntArray() arrayOf(it.width, it.height).toIntArray()
} }
} }
@@ -114,7 +114,7 @@ class ReaderAdapter(private val context: Context,
if (!isFullScreen) if (!isFullScreen)
(holder.view.container.layoutParams as ConstraintLayout.LayoutParams) (holder.view.container.layoutParams as ConstraintLayout.LayoutParams)
.dimensionRatio = "${reader!!.galleryInfo[position].width}:${reader!!.galleryInfo[position].height}" .dimensionRatio = "${reader!!.galleryInfo.files[position].width}:${reader!!.galleryInfo.files[position].height}"
holder.view.reader_index.text = (position+1).toString() holder.view.reader_index.text = (position+1).toString()
@@ -161,6 +161,6 @@ class ReaderAdapter(private val context: Context,
} }
} }
override fun getItemCount() = reader?.galleryInfo?.size ?: 0 override fun getItemCount() = reader?.galleryInfo?.files?.size ?: 0
} }

View File

@@ -263,8 +263,8 @@ class ReaderActivity : AppCompatActivity() {
notifyDataSetChanged() notifyDataSetChanged()
} }
title = reader.title title = reader.galleryInfo.title
menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${reader.galleryInfo.size}" menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${reader.galleryInfo.files.size}"
menu?.findItem(R.id.reader_type)?.icon = ContextCompat.getDrawable(this@ReaderActivity, menu?.findItem(R.id.reader_type)?.icon = ContextCompat.getDrawable(this@ReaderActivity,
when (reader.code) { when (reader.code) {

View File

@@ -22,6 +22,7 @@ import android.content.Context
import android.content.ContextWrapper import android.content.ContextWrapper
import android.util.Base64 import android.util.Base64
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.crashlytics.android.Crashlytics
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async
@@ -106,9 +107,11 @@ class Cache(context: Context) : ContextWrapper(context) {
var galleryBlock: GalleryBlock? = null var galleryBlock: GalleryBlock? = null
for (source in sources) { for (source in sources) {
galleryBlock = kotlin.runCatching { galleryBlock = try {
source.invoke() source.invoke()
}.getOrNull() } catch (e: Exception) {
null
}
if (galleryBlock != null) if (galleryBlock != null)
break break
@@ -158,6 +161,7 @@ class Cache(context: Context) : ContextWrapper(context) {
retval = try { retval = try {
source.value.invoke() source.value.invoke()
} catch (e: Exception) { } catch (e: Exception) {
Crashlytics.logException(e)
null null
} }

View File

@@ -213,7 +213,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
url( url(
urlFromUrlFromHash( urlFromUrlFromHash(
galleryID, galleryID,
reader.galleryInfo[index], reader.galleryInfo.files[index],
if (lowQuality) "webp" else null if (lowQuality) "webp" else null
) )
) )
@@ -251,18 +251,18 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
val cache = Cache(this@DownloadWorker).getImages(galleryID) val cache = Cache(this@DownloadWorker).getImages(galleryID)
progress.put(galleryID, reader.galleryInfo.indices.map { index -> progress.put(galleryID, reader.galleryInfo.files.indices.map { index ->
if (cache?.getOrNull(index) != null) if (cache?.getOrNull(index) != null)
Float.POSITIVE_INFINITY Float.POSITIVE_INFINITY
else else
0F 0F
}.toMutableList()) }.toMutableList())
exception.put(galleryID, reader.galleryInfo.map { null }.toMutableList()) exception.put(galleryID, reader.galleryInfo.files.map { null }.toMutableList())
if (notification[galleryID] == null) if (notification[galleryID] == null)
initNotification(galleryID) initNotification(galleryID)
notification[galleryID].setContentTitle(reader.title) notification[galleryID].setContentTitle(reader.galleryInfo.title)
notify(galleryID) notify(galleryID)
if (isCompleted(galleryID)) { if (isCompleted(galleryID)) {
@@ -276,7 +276,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
return@launch return@launch
} }
for (i in reader.galleryInfo.indices) { for (i in reader.galleryInfo.files.indices) {
val callback = object : Callback { val callback = object : Callback {
override fun onFailure(call: Call, e: IOException) { override fun onFailure(call: Call, e: IOException) {
if (Fabric.isInitialized() && e.message != "Canceled") if (Fabric.isInitialized() && e.message != "Canceled")

View File

@@ -10,4 +10,7 @@
<dimen name="nav_header_height">176dp</dimen> <dimen name="nav_header_height">176dp</dimen>
<dimen name="thumbnail_margin">8dp</dimen> <dimen name="thumbnail_margin">8dp</dimen>
<dimen name="galleryblock_thumbnail_thin">50dp</dimen>
<dimen name="galleryblock_thumbnail_normal">150dp</dimen>
</resources> </resources>

View File

@@ -17,7 +17,6 @@
package xyz.quaver.hitomi package xyz.quaver.hitomi
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.list
import xyz.quaver.proxy import xyz.quaver.proxy
import java.net.URL import java.net.URL
@@ -26,12 +25,10 @@ const val protocol = "https:"
@Suppress("EXPERIMENTAL_API_USAGE") @Suppress("EXPERIMENTAL_API_USAGE")
fun getGalleryInfo(galleryID: Int) = fun getGalleryInfo(galleryID: Int) =
Json.nonstrict.parse( Json.nonstrict.parse(
GalleryInfo.serializer().list, GalleryInfo.serializer(),
Regex("""\[.+]""").find(
URL("$protocol//$domain/galleries/$galleryID.js").openConnection(proxy).getInputStream().use { URL("$protocol//$domain/galleries/$galleryID.js").openConnection(proxy).getInputStream().use {
it.reader().readText() it.reader().readText()
} }.replace("var galleryinfo = ", "")
)?.value ?: "[]"
) )
//common.js //common.js
@@ -71,6 +68,7 @@ fun urlFromURL(url: String, base: String? = null) : String {
return url.replace(Regex("""//..?\.hitomi\.la/"""), "//${subdomainFromURL(url, base)}.hitomi.la/") return url.replace(Regex("""//..?\.hitomi\.la/"""), "//${subdomainFromURL(url, base)}.hitomi.la/")
} }
fun fullPathFromHash(hash: String?) : String? { fun fullPathFromHash(hash: String?) : String? {
return when { return when {
(hash?.length ?: 0) < 3 -> hash (hash?.length ?: 0) < 3 -> hash
@@ -79,11 +77,20 @@ fun fullPathFromHash(hash: String?) : String? {
} }
@Suppress("NAME_SHADOWING", "UNUSED_PARAMETER") @Suppress("NAME_SHADOWING", "UNUSED_PARAMETER")
fun urlFromHash(galleryID: Int, image: GalleryInfo, dir: String? = null, ext: String? = null) : String { fun urlFromHash(galleryID: Int, image: GalleryFiles, dir: String? = null, ext: String? = null) : String {
val ext = ext ?: dir ?: image.name.split('.').last() val ext = ext ?: dir ?: image.name.split('.').last()
val dir = dir ?: "images" val dir = dir ?: "images"
return "$protocol//a.hitomi.la/$dir/${fullPathFromHash(image.hash)}.$ext" return "$protocol//a.hitomi.la/$dir/${fullPathFromHash(image.hash)}.$ext"
} }
fun urlFromUrlFromHash(galleryID: Int, image: GalleryInfo, dir: String? = null, ext: String? = null, base: String? = null) = fun urlFromUrlFromHash(galleryID: Int, image: GalleryFiles, dir: String? = null, ext: String? = null, base: String? = null) =
urlFromURL(urlFromHash(galleryID, image, dir, ext), base) urlFromURL(urlFromHash(galleryID, image, dir, ext), base)
fun imageUrlFromImage(galleryID: Int, image: GalleryFiles, noWebp: Boolean) : String {
val webp = if (image.hash != null && image.haswebp != 0 && !noWebp)
"webp"
else
null
return urlFromUrlFromHash(galleryID, image, webp)
}

View File

@@ -71,7 +71,7 @@ fun getGallery(galleryID: Int) : Gallery {
href.slice(5 until href.indexOf('-')) href.slice(5 until href.indexOf('-'))
} }
val thumbnails = getGalleryInfo(galleryID).map { galleryInfo -> val thumbnails = getGalleryInfo(galleryID).files.map { galleryInfo ->
urlFromUrlFromHash(galleryID, galleryInfo, "smalltn", "jpg", "tn") urlFromUrlFromHash(galleryID, galleryInfo, "smalltn", "jpg", "tn")
} }

View File

@@ -17,29 +17,35 @@
package xyz.quaver.hitomi package xyz.quaver.hitomi
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import org.jsoup.Jsoup
import xyz.quaver.Code import xyz.quaver.Code
import xyz.quaver.proxy
fun getReferer(galleryID: Int) = "https://hitomi.la/reader/$galleryID.html" fun getReferer(galleryID: Int) = "https://hitomi.la/reader/$galleryID.html"
@Serializable @Serializable
data class GalleryInfo( data class GalleryInfo(
val language_localname: String? = null,
val language: String? = null,
val date: String? = null,
val files: List<GalleryFiles>,
val id: Int? = null,
val type: String? = null,
val title: String? = null
)
@Serializable
data class GalleryFiles(
val width: Int, val width: Int,
val hash: String? = null, val hash: String? = null,
val haswebp: Int = 0, val haswebp: Int = 0,
val name: String, val name: String,
val height: Int val height: Int,
val hasavif: Int = 0
) )
@Serializable @Serializable
data class Reader(val code: Code, val title: String, val galleryInfo: List<GalleryInfo>) data class Reader(val code: Code, val galleryInfo: GalleryInfo)
//Set header `Referer` to reader url to avoid 403 error //Set header `Referer` to reader url to avoid 403 error
fun getReader(galleryID: Int) : Reader { fun getReader(galleryID: Int) : Reader {
val readerUrl = "https://hitomi.la/reader/$galleryID.html" return Reader(Code.HITOMI, getGalleryInfo(galleryID))
val doc = Jsoup.connect(readerUrl).proxy(proxy).get()
return Reader(Code.HITOMI, doc.title(), getGalleryInfo(galleryID))
} }

View File

@@ -21,6 +21,7 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.list import kotlinx.serialization.list
import org.jsoup.Jsoup import org.jsoup.Jsoup
import xyz.quaver.Code import xyz.quaver.Code
import xyz.quaver.hitomi.GalleryFiles
import xyz.quaver.hitomi.GalleryInfo import xyz.quaver.hitomi.GalleryInfo
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
import xyz.quaver.hitomi.protocol import xyz.quaver.hitomi.protocol
@@ -63,12 +64,12 @@ fun renewCookie() : String {
@UseExperimental(UnstableDefault::class) @UseExperimental(UnstableDefault::class)
fun getReader(galleryID: Int) : Reader { fun getReader(galleryID: Int) : Reader {
val reader = "https://$hiyobi/reader/$galleryID" val reader = "https://$hiyobi/reader/$galleryID"
val url = "https://$hiyobi/data/json/${galleryID}_list.json" val url = "https://cdn.hiyobi.me/data/json/${galleryID}_list.json"
val title = Jsoup.connect(reader).proxy(proxy).get().title() val title = Jsoup.connect(reader).proxy(proxy).get().title()
val galleryInfo = Json.nonstrict.parse( val galleryFiles = Json.nonstrict.parse(
GalleryInfo.serializer().list, GalleryFiles.serializer().list,
with(URL(url).openConnection(proxy) as HttpsURLConnection) { with(URL(url).openConnection(proxy) as HttpsURLConnection) {
setRequestProperty("User-Agent", user_agent) setRequestProperty("User-Agent", user_agent)
setRequestProperty("Cookie", cookie) setRequestProperty("Cookie", cookie)
@@ -79,14 +80,14 @@ fun getReader(galleryID: Int) : Reader {
} }
) )
return Reader(Code.HIYOBI, title, galleryInfo) return Reader(Code.HIYOBI, GalleryInfo(title = title, files = galleryFiles))
} }
fun createImgList(galleryID: Int, reader: Reader, lowQuality: Boolean = false) = fun createImgList(galleryID: Int, reader: Reader, lowQuality: Boolean = false) =
if (lowQuality) if (lowQuality)
reader.galleryInfo.map { reader.galleryInfo.files.map {
val name = it.name.replace(Regex("""\.[^/.]+$"""), "") val name = it.name.replace(Regex("""\.[^/.]+$"""), "")
Images("$protocol//$hiyobi/data_r/$galleryID/$name.jpg", galleryID, it.name) Images("$protocol//$hiyobi/data_r/$galleryID/$name.jpg", galleryID, it.name)
} }
else else
reader.galleryInfo.map { Images("$protocol//$hiyobi/data/$galleryID/${it.name}", galleryID, it.name) } reader.galleryInfo.files.map { Images("$protocol//$hiyobi/data/$galleryID/${it.name}", galleryID, it.name) }

View File

@@ -82,14 +82,14 @@ class UnitTest {
@Test @Test
fun test_hiyobi() { fun test_hiyobi() {
val reader = xyz.quaver.hiyobi.getReader(10000062) val reader = xyz.quaver.hiyobi.getReader(1574736)
print(reader) print(reader)
} }
@Test @Test
fun test_urlFromUrlFromHash() { fun test_urlFromUrlFromHash() {
val url = urlFromUrlFromHash(1531795, GalleryInfo( val url = urlFromUrlFromHash(1531795, GalleryFiles(
212, "719d46a7556be0d0021c5105878507129b5b3308b02cf67f18901b69dbb3b5ef", 1, "00.jpg", 300 212, "719d46a7556be0d0021c5105878507129b5b3308b02cf67f18901b69dbb3b5ef", 1, "00.jpg", 300
), "webp") ), "webp")