Added Offline mode
This commit is contained in:
@@ -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<URL>,
|
||||
val thumbnails: List<String>,
|
||||
val title: String,
|
||||
val artists: List<String>,
|
||||
val series: List<String>,
|
||||
@@ -53,27 +55,31 @@ data class GalleryBlock(
|
||||
val language: String,
|
||||
val relatedTags: List<String>
|
||||
)
|
||||
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)
|
||||
}
|
||||
@@ -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<GalleryInfo?>()
|
||||
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?>()
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -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<Int> {
|
||||
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<Suggestion> {
|
||||
}
|
||||
|
||||
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<Long, Int>) : List<Suggestion> {
|
||||
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<Suggestion>()
|
||||
|
||||
@@ -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<Long, Int>) : List<Int> {
|
||||
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<Int>()
|
||||
|
||||
@@ -200,6 +210,11 @@ fun getGalleryIDsFromData(data: Pair<Long, Int>) : List<Int> {
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user