Hiyobi file structure changed
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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":{}}]
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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))
|
|
||||||
}
|
}
|
||||||
@@ -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) }
|
||||||
@@ -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")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user