From d090a59800ed0e8c4b5d9758355c30faf579dd17 Mon Sep 17 00:00:00 2001 From: tom5079 Date: Thu, 16 May 2019 18:15:53 +0900 Subject: [PATCH] Added Offline mode --- .../java/xyz/quaver/pupil/MainActivity.kt | 60 +++++++++++++----- .../java/xyz/quaver/pupil/ReaderActivity.kt | 60 +++++++++++------- .../pupil/adapters/GalleryBlockAdapter.kt | 6 +- .../main/java/xyz/quaver/pupil/util/update.kt | 8 ++- .../res/drawable-anydpi/ic_placeholder.xml | 10 +++ .../main/res/drawable-hdpi/ic_placeholder.png | Bin 0 -> 361 bytes .../main/res/drawable-mdpi/ic_placeholder.png | Bin 0 -> 224 bytes .../res/drawable-xhdpi/ic_placeholder.png | Bin 0 -> 370 bytes .../res/drawable-xxhdpi/ic_placeholder.png | Bin 0 -> 602 bytes .../java/xyz/quaver/hitomi/galleryblock.kt | 42 ++++++------ .../main/java/xyz/quaver/hitomi/readers.kt | 37 ++++++----- .../src/main/java/xyz/quaver/hitomi/search.kt | 37 +++++++---- .../src/main/java/xyz/quaver/hiyobi/reader.kt | 42 +++++++----- .../test/java/xyz/quaver/hitomi/UnitTest.kt | 7 +- 14 files changed, 201 insertions(+), 108 deletions(-) create mode 100644 app/src/main/res/drawable-anydpi/ic_placeholder.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_placeholder.png create mode 100644 app/src/main/res/drawable-mdpi/ic_placeholder.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_placeholder.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_placeholder.png diff --git a/app/src/main/java/xyz/quaver/pupil/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/MainActivity.kt index 0d97193c..6e9f18f7 100644 --- a/app/src/main/java/xyz/quaver/pupil/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/MainActivity.kt @@ -6,7 +6,6 @@ import android.os.Bundle import android.preference.PreferenceManager import android.text.* import android.text.style.AlignmentSpan -import android.util.Log import android.view.View import android.view.WindowManager import androidx.appcompat.app.AlertDialog @@ -22,6 +21,8 @@ import com.google.android.material.appbar.AppBarLayout import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main_content.* import kotlinx.coroutines.* +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonConfiguration import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.content import ru.noties.markwon.Markwon @@ -34,6 +35,7 @@ import xyz.quaver.pupil.util.SetLineOverlap import xyz.quaver.pupil.util.checkUpdate import java.io.File import java.io.FileOutputStream +import java.net.URL import java.util.* import javax.net.ssl.HttpsURLConnection import kotlin.collections.ArrayList @@ -436,25 +438,48 @@ class MainActivity : AppCompatActivity() { galleryIDs else -> galleryIDs.slice(galleries.size until Math.min(galleries.size+perPage, galleryIDs.size)) - }.chunked(4).let { chunks -> + }.chunked(5).let { chunks -> for (chunk in chunks) chunk.map { async { try { - val galleryBlock = getGalleryBlock(it) + val json = Json(JsonConfiguration.Stable) + val serializer = GalleryBlock.serializer() + + val galleryBlock = + File(cacheDir, "imageCache/$it/galleryBlock.json").let { cache -> + when { + cache.exists() -> json.parse(serializer, cache.readText()) + else -> { + getGalleryBlock(it).apply { + this ?: return@apply + + if (!cache.parentFile.exists()) + cache.parentFile.mkdirs() + + cache.writeText(json.stringify(serializer, this)) + } + } + } + } ?: return@async null val thumbnail = async { - val cache = File(cacheDir, "imageCache/$it/thumbnail.${galleryBlock.thumbnails[0].path.split('.').last()}") + val ext = galleryBlock.thumbnails[0].split('.').last() + File(cacheDir, "imageCache/$it/thumbnail.$ext").apply { + val cache = this - if (!cache.exists()) - with(galleryBlock.thumbnails[0].openConnection() as HttpsURLConnection) { - if (!cache.parentFile.exists()) - cache.parentFile.mkdirs() + if (!cache.exists()) + try { + with(URL(galleryBlock.thumbnails[0]).openConnection() as HttpsURLConnection) { + if (!cache.parentFile.exists()) + cache.parentFile.mkdirs() - inputStream.copyTo(FileOutputStream(cache)) - } - - cache.absolutePath + inputStream.copyTo(FileOutputStream(cache)) + } + } catch (e: Exception) { + cache.delete() + } + }.absolutePath } Pair(galleryBlock, thumbnail) @@ -463,16 +488,19 @@ class MainActivity : AppCompatActivity() { } } }.forEach { - val galleryBlock = it.await() ?: return@forEach + val galleryBlock = it.await() withContext(Dispatchers.Main) { main_progressbar.hide() - galleries.add(galleryBlock) - main_recyclerview.adapter?.notifyItemInserted(galleries.size - 1) + if (galleryBlock != null) { + galleries.add(galleryBlock) + + main_recyclerview.adapter?.notifyItemInserted(galleries.size - 1) + } } } - } + } } } } diff --git a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt index f6e56537..94f5fa8d 100644 --- a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt @@ -66,26 +66,36 @@ class ReaderActivity : AppCompatActivity() { else -> File(cacheDir, "imageCache/$galleryID/reader.json") } - if (cache.exists()) - json.parse(serializer, cache.readText()) - else { - val reader = when { - isHiyobi -> { - try { - xyz.quaver.hiyobi.getReader(galleryID) - } catch (e: Exception) { - getReader(galleryID) - } - } - else -> { - getReader(galleryID) - } - } + if (cache.exists()) { + val cached = json.parse(serializer, cache.readText()) - cache.writeText(json.stringify(serializer, reader)) - - reader + if (cached.isNotEmpty()) + return@async cached } + + val reader = when { + isHiyobi -> { + xyz.quaver.hiyobi.getReader(galleryID).let { + when { + it.isEmpty() -> getReader(galleryID) + else -> it + } + } + } + else -> { + getReader(galleryID) + } + } + + if (reader.isEmpty()) + finish() + + if (!cache.parentFile.exists()) + cache.parentFile.mkdirs() + + cache.writeText(json.stringify(serializer, reader)) + + reader } initView() @@ -257,13 +267,17 @@ class ReaderActivity : AppCompatActivity() { val cache = File(cacheDir, "/imageCache/$galleryID/$fileName") if (!cache.exists()) - with(URL(url).openConnection() as HttpsURLConnection) { - setRequestProperty("Referer", getReferer(galleryID)) + try { + with(URL(url).openConnection() as HttpsURLConnection) { + setRequestProperty("Referer", getReferer(galleryID)) - if (!cache.parentFile.exists()) - cache.parentFile.mkdirs() + if (!cache.parentFile.exists()) + cache.parentFile.mkdirs() - inputStream.copyTo(FileOutputStream(cache)) + inputStream.copyTo(FileOutputStream(cache)) + } + } catch (e: Exception) { + cache.delete() } cache.absolutePath diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt index e83e4f86..c32ffb78 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt @@ -9,11 +9,13 @@ import androidx.cardview.widget.CardView import androidx.recyclerview.widget.RecyclerView import com.google.android.material.chip.Chip import kotlinx.android.synthetic.main.item_galleryblock.view.* -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import xyz.quaver.hitomi.GalleryBlock import xyz.quaver.hitomi.toTag import xyz.quaver.pupil.R -import java.io.File class GalleryBlockAdapter(private val galleries: List>>) : RecyclerView.Adapter() { diff --git a/app/src/main/java/xyz/quaver/pupil/util/update.kt b/app/src/main/java/xyz/quaver/pupil/util/update.kt index b25e03e0..2c951cbe 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/update.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/update.kt @@ -4,8 +4,12 @@ import kotlinx.serialization.json.* import java.net.URL fun getReleases(url: String) : JsonArray { - return URL(url).readText().let { - Json(JsonConfiguration.Stable).parse(JsonArray.serializer(), it) + return try { + URL(url).readText().let { + Json(JsonConfiguration.Stable).parse(JsonArray.serializer(), it) + } + } catch (e: Exception) { + JsonArray(emptyList()) } } diff --git a/app/src/main/res/drawable-anydpi/ic_placeholder.xml b/app/src/main/res/drawable-anydpi/ic_placeholder.xml new file mode 100644 index 00000000..7b74d4b6 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_placeholder.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable-hdpi/ic_placeholder.png b/app/src/main/res/drawable-hdpi/ic_placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..1604365ee4cfd9686abc9af4c6b8b3889469cb9e GIT binary patch literal 361 zcmV-v0ha!WP)!clwb=yPI z0oeAC#D40e{`!dUr@aUIDbVJA@;7%4+T2BPlUHdkUKH{I7AE#Niuw;m00000NkvXX Hu0mjfnWma` literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_placeholder.png b/app/src/main/res/drawable-mdpi/ic_placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..cc9eb1eb4c55dfe2c692efd200d7a0f7e8659df5 GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gj%ROBjLn>~)o$ATgpupq0x9@P< zOTK&mIW{#0Ye-~E1#?-fS#q+!yZ^tT$lG0Erm9QlObs&2*sCdRJgLDTp;CY^%HqJU zwtt)-+PiA_99k>ZeifPesn;-BI&6AzSe%zQ#c{Xr@h1B+=m|`PCO4b zKa5debrCLNNIcJ=Kl7H5jdPNB?2ePQ>$*3!&-xhaIXi4?>h6>4nfCpSssDF4)AcHw Y@5IBCK0e!d1n6W2Pgg&ebxsLQ0RH1!<^TWy literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_placeholder.png b/app/src/main/res/drawable-xhdpi/ic_placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..2a7d65cbccfd703475685da900a5b5acfcad4e2a GIT binary patch literal 370 zcmV-&0ge8NP)Wpo<8gLjZ=Df&c^{00I63KuiH2SgHdw z1c0cj>LLS#XczhI5de*=`8gMWXxI69oA#2(}D*#}@#-hfJD+~m3&ZWGB>%)jQ4|$KADQ)&`{JLRKd_g89(ZGvRD?wMzrK;27glBXtVKXw$suzp(?g)Hn=CodU7+vPO|8fwqZA zRvbbkD~^G*vmueTU0usH7KZ>y;p{xF{|^#*<30k(3nu}|3uj1Kmha~HEO$4?mTM|b z1X8Ca96G*y&wd!6?f#etQWQ=AQXhkJi^O^p@pid};0))WNF;@`cWo;2HLtGm+3&QP z%QX!|o=!PFG87-li5<2TLOgQ=wJxO8 zFWr>y1droK!;0i{S|!-FqmTlQ9gTeL6DLsc!7Sq77lfpD(vX8|BkPe9$Qvo)r-K(# z1P&ik=qZ-q$LA!H-Z{I3@f$hD19v~Dvu(SC?F%cCReKmY;|fB*y_00HQ~f$%7T0|*bZIQqVL9BL|fpw1(B oB-3*x;h|mxK@bE%5CoAq1CeP~KZTO{AOHXW07*qoM6N<$f-O7$MgRZ+ literal 0 HcmV?d00001 diff --git a/libpupil/src/main/java/xyz/quaver/hitomi/galleryblock.kt b/libpupil/src/main/java/xyz/quaver/hitomi/galleryblock.kt index 39dfb263..cd4859b0 100644 --- a/libpupil/src/main/java/xyz/quaver/hitomi/galleryblock.kt +++ b/libpupil/src/main/java/xyz/quaver/hitomi/galleryblock.kt @@ -1,5 +1,6 @@ package xyz.quaver.hitomi +import kotlinx.serialization.Serializable import org.jsoup.Jsoup import java.net.URL import java.net.URLDecoder @@ -39,13 +40,14 @@ fun fetchNozomi(area: String? = null, tag: String = "index", language: String = return nozomi } } catch (e: Exception) { - return listOf() + return emptyList() } } +@Serializable data class GalleryBlock( val id: Int, - val thumbnails: List, + val thumbnails: List, val title: String, val artists: List, val series: List, @@ -53,27 +55,31 @@ data class GalleryBlock( val language: String, val relatedTags: List ) -fun getGalleryBlock(galleryID: Int) : GalleryBlock { +fun getGalleryBlock(galleryID: Int) : GalleryBlock? { val url = "$protocol//$domain/$galleryblockdir/$galleryID$extension" - val doc = Jsoup.connect(url).get() + try { + val doc = Jsoup.connect(url).get() - val thumbnails = doc.select("img").map { URL(protocol + it.attr("data-src")) } + val thumbnails = doc.select("img").map { protocol + it.attr("data-src") } - val title = doc.selectFirst("h1.lillie > a").text() - val artists = doc.select("div.artist-list a").map{ it.text() } - val series = doc.select("a[href~=^/series/]").map { it.text() } - val type = doc.selectFirst("a[href~=^/type/]").text() + val title = doc.selectFirst("h1.lillie > a").text() + val artists = doc.select("div.artist-list a").map{ it.text() } + val series = doc.select("a[href~=^/series/]").map { it.text() } + val type = doc.selectFirst("a[href~=^/type/]").text() - val language = { - val href = doc.select("a[href~=^/index-.+-1.html]").attr("href") - href.slice(7 until href.indexOf("-1")) - }.invoke() + val language = { + val href = doc.select("a[href~=^/index-.+-1.html]").attr("href") + href.slice(7 until href.indexOf("-1")) + }.invoke() - val relatedTags = doc.select(".relatedtags a").map { - val href = URLDecoder.decode(it.attr("href"), "UTF-8") - href.slice(5 until href.indexOf('-')) + val relatedTags = doc.select(".relatedtags a").map { + val href = URLDecoder.decode(it.attr("href"), "UTF-8") + href.slice(5 until href.indexOf('-')) + } + + return GalleryBlock(galleryID, thumbnails, title, artists, series, type, language, relatedTags) + } catch (e: Exception) { + return null } - - return GalleryBlock(galleryID, thumbnails, title, artists, series, type, language, relatedTags) } \ No newline at end of file diff --git a/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt b/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt index f2a06ec1..36b9811d 100644 --- a/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt +++ b/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt @@ -27,27 +27,32 @@ fun getReader(galleryID: Int) : Reader { val readerUrl = "https://hitomi.la/reader/$galleryID.html" val galleryInfoUrl = "https://ltn.hitomi.la/galleries/$galleryID.js" - val doc = Jsoup.connect(readerUrl).get() + try { - val images = doc.select(".img-url").map { - protocol + urlFromURL(it.text()) - } + val doc = Jsoup.connect(readerUrl).get() - val galleryInfo = ArrayList() + val images = doc.select(".img-url").map { + protocol + urlFromURL(it.text()) + } - galleryInfo.addAll( - Json(JsonConfiguration.Stable).parse( - GalleryInfo.serializer().list, - Regex("""\[.+]""").find( - URL(galleryInfoUrl).readText() - )?.value ?: "[]" + val galleryInfo = ArrayList() + + galleryInfo.addAll( + Json(JsonConfiguration.Stable).parse( + GalleryInfo.serializer().list, + Regex("""\[.+]""").find( + URL(galleryInfoUrl).readText() + )?.value ?: "[]" + ) ) - ) - if (images.size > galleryInfo.size) - galleryInfo.addAll(arrayOfNulls(images.size - galleryInfo.size)) + if (images.size > galleryInfo.size) + galleryInfo.addAll(arrayOfNulls(images.size - galleryInfo.size)) - return (images zip galleryInfo).map { - ReaderItem(it.first, it.second) + return (images zip galleryInfo).map { + ReaderItem(it.first, it.second) + } + } catch (e: Exception) { + return emptyList() } } \ No newline at end of file diff --git a/libpupil/src/main/java/xyz/quaver/hitomi/search.kt b/libpupil/src/main/java/xyz/quaver/hitomi/search.kt index e771ec3d..833c826c 100644 --- a/libpupil/src/main/java/xyz/quaver/hitomi/search.kt +++ b/libpupil/src/main/java/xyz/quaver/hitomi/search.kt @@ -15,8 +15,8 @@ const val max_node_size = 464 const val B = 16 const val compressed_nozomi_prefix = "n" -val tag_index_version = getIndexVersion("tagindex") -val galleries_index_version = getIndexVersion("galleriesindex") +var tag_index_version = getIndexVersion("tagindex") +var galleries_index_version = getIndexVersion("galleriesindex") fun sha256(data: ByteArray) : ByteArray { return MessageDigest.getInstance("SHA-256").digest(data) @@ -32,8 +32,12 @@ fun sanitize(input: String) : String { } fun getIndexVersion(name: String) : String { - return URL("$protocol//$domain/$name/version?_=${System.currentTimeMillis()}") - .readText() + return try { + URL("$protocol//$domain/$name/version?_=${System.currentTimeMillis()}") + .readText() + } catch (e: Exception) { + "" + } } //search.js @@ -64,14 +68,14 @@ fun getGalleryIDsForQuery(query: String) : List { val key = hashTerm(it) val field = "galleries" - val node = getNodeAtAddress(field, 0) ?: return listOf() + val node = getNodeAtAddress(field, 0) ?: return emptyList() val data = bSearch(field, key, node) if (data != null) return getGalleryIDsFromData(data) - return arrayListOf() + return emptyList() } } @@ -87,24 +91,27 @@ fun getSuggestionsForQuery(query: String) : List { } val key = hashTerm(term) - val node = getNodeAtAddress(field, 0) ?: return listOf() + val node = getNodeAtAddress(field, 0) ?: return emptyList() val data = bSearch(field, key, node) if (data != null) return getSuggestionsFromData(field, data) - return listOf() + return emptyList() } } data class Suggestion(val s: String, val t: Int, val u: String, val n: String) fun getSuggestionsFromData(field: String, data: Pair) : List { + if (tag_index_version.isEmpty()) + tag_index_version = getIndexVersion("tagindex") + val url = "$protocol//$domain/$index_dir/$field.$tag_index_version.data" val (offset, length) = data if (length > 10000 || length <= 0) throw Exception("length $length is too long") - val inbuf = getURLAtRange(url, offset.until(offset+length)) ?: return listOf() + val inbuf = getURLAtRange(url, offset.until(offset+length)) ?: return emptyList() val suggestions = ArrayList() @@ -166,17 +173,20 @@ fun getGalleryIDsFromNozomi(area: String?, tag: String, language: String) : List return nozomi } } catch (e: Exception) { - return listOf() + return emptyList() } } fun getGalleryIDsFromData(data: Pair) : List { + if (galleries_index_version.isEmpty()) + galleries_index_version = getIndexVersion("galleriesindex") + val url = "$protocol//$domain/$galleries_index_dir/galleries.$galleries_index_version.data" val (offset, length) = data if (length > 100000000 || length <= 0) throw Exception("length $length is too long") - val inbuf = getURLAtRange(url, offset.until(offset+length)) ?: return listOf() + val inbuf = getURLAtRange(url, offset.until(offset+length)) ?: return emptyList() val galleryIDs = ArrayList() @@ -200,6 +210,11 @@ fun getGalleryIDsFromData(data: Pair) : List { } fun getNodeAtAddress(field: String, address: Long) : Node? { + if (tag_index_version.isEmpty()) + tag_index_version = getIndexVersion("tagindex") + if (galleries_index_version.isEmpty()) + galleries_index_version = getIndexVersion("galleriesindex") + val url = when(field) { "galleries" -> "$protocol//$domain/$galleries_index_dir/galleries.$galleries_index_version.index" diff --git a/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt b/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt index 47f766b4..2f201385 100644 --- a/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt +++ b/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt @@ -16,11 +16,15 @@ var cookie: String = "" fun renewCookie() : String { val url = "https://$hiyobi/" - with(URL(url).openConnection() as HttpsURLConnection) { - setRequestProperty("User-Agent", user_agent) - connectTimeout = 2000 - connect() - return headerFields["Set-Cookie"]!![0] + try { + with(URL(url).openConnection() as HttpsURLConnection) { + setRequestProperty("User-Agent", user_agent) + connectTimeout = 2000 + connect() + return headerFields["Set-Cookie"]!![0] + } + } catch (e: Exception) { + return "" } } @@ -30,19 +34,23 @@ fun getReader(galleryId: Int) : Reader { if (cookie.isEmpty()) cookie = renewCookie() - val json = Json(JsonConfiguration.Stable).parseJson( - with(URL(url).openConnection() as HttpsURLConnection) { - setRequestProperty("User-Agent", user_agent) - setRequestProperty("Cookie", cookie) - connectTimeout = 2000 - connect() + try { + val json = Json(JsonConfiguration.Stable).parseJson( + with(URL(url).openConnection() as HttpsURLConnection) { + setRequestProperty("User-Agent", user_agent) + setRequestProperty("Cookie", cookie) + connectTimeout = 2000 + connect() - inputStream.bufferedReader().use { it.readText() } + inputStream.bufferedReader().use { it.readText() } + } + ) + + return json.jsonArray.map { + val name = it.jsonObject["name"]!!.content + ReaderItem("https://$hiyobi/data/$galleryId/$name", null) } - ) - - return json.jsonArray.map { - val name = it.jsonObject["name"]!!.content - ReaderItem("https://$hiyobi/data/$galleryId/$name", null) + } catch (e: Exception) { + return emptyList() } } \ No newline at end of file diff --git a/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt b/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt index e32567f5..b573e193 100644 --- a/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt +++ b/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt @@ -1,14 +1,15 @@ package xyz.quaver.hitomi import org.junit.Test +import java.io.File import java.net.URL class UnitTest { @Test fun test() { - val url = URL("https://ltn.hitomi.la/galleries/1411672.js") + val f = File("C:/Users/tom50/Workspace/Pupil/nodir/nodir/asdf.txt") - print(url.path.substring(url.path.lastIndexOf('/')+1)) + f.delete() } @Test @@ -63,6 +64,6 @@ class UnitTest { @Test fun test_hiyobi() { - xyz.quaver.hiyobi.getReader(1414061) + print(xyz.quaver.hiyobi.getReader(1415416).size) } } \ No newline at end of file