Fixed bug caused by updated hitomi server structure
Version 4.0
This commit is contained in:
12
.idea/codeStyles/Project.xml
generated
12
.idea/codeStyles/Project.xml
generated
@@ -1,5 +1,8 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<AndroidXmlCodeStyleSettings>
|
||||
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
|
||||
</AndroidXmlCodeStyleSettings>
|
||||
<JetCodeStyleSettings>
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
@@ -14,6 +17,7 @@
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
@@ -24,6 +28,7 @@
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
@@ -35,6 +40,7 @@
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
@@ -45,6 +51,7 @@
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
@@ -55,6 +62,7 @@
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
@@ -65,6 +73,7 @@
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
@@ -75,6 +84,7 @@
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
@@ -86,6 +96,7 @@
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
@@ -97,6 +108,7 @@
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
|
||||
@@ -13,8 +13,8 @@ android {
|
||||
applicationId "xyz.quaver.pupil"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 29
|
||||
versionCode 27
|
||||
versionName "3.2"
|
||||
versionCode 29
|
||||
versionName "4.0"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
multiDexEnabled true
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
@@ -26,7 +26,7 @@ android {
|
||||
}
|
||||
buildTypes.each {
|
||||
it.buildConfigField('boolean', 'PRERELEASE', 'false')
|
||||
it.buildConfigField('boolean', 'CENSOR', 'true')
|
||||
it.buildConfigField('boolean', 'CENSOR', 'false')
|
||||
}
|
||||
}
|
||||
kotlinOptions {
|
||||
@@ -47,19 +47,20 @@ dependencies {
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1'
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.11.0"
|
||||
implementation 'androidx.appcompat:appcompat:1.0.2'
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.preference:preference:1.1.0-rc01'
|
||||
implementation 'androidx.preference:preference:1.1.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.0.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation "androidx.biometric:biometric:1.0.0-rc02"
|
||||
implementation 'com.android.support:multidex:1.0.3'
|
||||
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
||||
implementation 'com.google.android.material:material:1.1.0-alpha09'
|
||||
implementation 'com.google.firebase:firebase-core:17.1.0'
|
||||
implementation 'com.google.firebase:firebase-perf:19.0.0'
|
||||
implementation 'com.google.android.material:material:1.2.0-alpha01'
|
||||
implementation 'com.google.firebase:firebase-core:17.2.1'
|
||||
implementation 'com.google.firebase:firebase-perf:19.0.1'
|
||||
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
|
||||
implementation 'com.github.arimorty:floatingsearchview:2.1.1'
|
||||
implementation 'com.github.clans:fab:1.6.4'
|
||||
|
||||
@@ -1 +1 @@
|
||||
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":27,"versionName":"3.2","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
|
||||
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":29,"versionName":"4.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
|
||||
@@ -26,11 +26,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.rule.ActivityTestRule
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import xyz.quaver.hitomi.fetchNozomi
|
||||
import xyz.quaver.hiyobi.cookie
|
||||
import xyz.quaver.hiyobi.createImgList
|
||||
import xyz.quaver.hiyobi.getReader
|
||||
import xyz.quaver.hiyobi.user_agent
|
||||
import xyz.quaver.pupil.ui.LockActivity
|
||||
@@ -50,8 +49,6 @@ class ExampleInstrumentedTest {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("xyz.quaver.pupil", appContext.packageName)
|
||||
|
||||
Log.d("Pupil", fetchNozomi().first.size.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -70,7 +67,7 @@ class ExampleInstrumentedTest {
|
||||
|
||||
val data: ByteArray
|
||||
|
||||
with(URL(reader.readerItems[0].url).openConnection() as HttpsURLConnection) {
|
||||
with(URL(createImgList(1426382, reader)[0].path).openConnection() as HttpsURLConnection) {
|
||||
setRequestProperty("User-Agent", user_agent)
|
||||
setRequestProperty("Cookie", cookie)
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="xyz.quaver.pupil">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28"/>
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
|
||||
|
||||
<application
|
||||
android:name=".Pupil"
|
||||
@@ -15,7 +17,8 @@
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
android:theme="@style/AppTheme"
|
||||
tools:replace="android:theme">
|
||||
|
||||
<activity android:name=".ui.LockActivity"/>
|
||||
<activity
|
||||
|
||||
@@ -114,7 +114,7 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
|
||||
.parse(Reader.serializer(), readerCache.invoke().readText())
|
||||
|
||||
with(galleryblock_progressbar) {
|
||||
max = reader.readerItems.size
|
||||
max = reader.galleryInfo.size
|
||||
progress = imageCache.invoke().list()?.size ?: 0
|
||||
|
||||
visibility = View.VISIBLE
|
||||
@@ -139,7 +139,7 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
|
||||
if (visibility == View.GONE) {
|
||||
val reader = Json(JsonConfiguration.Stable)
|
||||
.parse(Reader.serializer(), readerCache.invoke().readText())
|
||||
max = reader.readerItems.size
|
||||
max = reader.galleryInfo.size
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
|
||||
@@ -109,11 +109,11 @@ class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(conte
|
||||
}
|
||||
|
||||
Glide.with(context)
|
||||
.load(gallery.thumbnails.firstOrNull())
|
||||
.load(gallery.cover)
|
||||
.apply {
|
||||
if (BuildConfig.CENSOR)
|
||||
override(5, 8)
|
||||
}.into(gallery_thumbnail)
|
||||
}.into(gallery_cover)
|
||||
|
||||
addDetails(gallery)
|
||||
addThumbnails(gallery)
|
||||
|
||||
@@ -371,7 +371,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun checkPermissions() {
|
||||
if (this.hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE))
|
||||
if (!hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE))
|
||||
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 13489)
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,6 @@ import com.andrognito.patternlockview.utils.PatternLockUtils
|
||||
import kotlinx.android.synthetic.main.fragment_pattern_lock.*
|
||||
import kotlinx.android.synthetic.main.fragment_pattern_lock.view.*
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.util.hash
|
||||
import xyz.quaver.pupil.util.hashWithSalt
|
||||
|
||||
class PatternLockFragment : Fragment(), PatternLockViewListener {
|
||||
|
||||
|
||||
@@ -249,16 +249,16 @@ class ReaderActivity : AppCompatActivity() {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
title = it.title
|
||||
with(reader_download_progressbar) {
|
||||
max = it.readerItems.size
|
||||
max = it.galleryInfo.size
|
||||
progress = 0
|
||||
}
|
||||
with(reader_progressbar) {
|
||||
max = it.readerItems.size
|
||||
max = it.galleryInfo.size
|
||||
progress = 0
|
||||
}
|
||||
|
||||
gallerySize = it.readerItems.size
|
||||
menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${it.readerItems.size}"
|
||||
gallerySize = it.galleryInfo.size
|
||||
menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${it.galleryInfo.size}"
|
||||
}
|
||||
}
|
||||
onProgressHandler = {
|
||||
|
||||
@@ -29,13 +29,15 @@ import androidx.core.app.TaskStackBuilder
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.crashlytics.android.Crashlytics
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.io.IOException
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
import xyz.quaver.availableInHiyobi
|
||||
import xyz.quaver.hitomi.Reader
|
||||
import xyz.quaver.hitomi.getReader
|
||||
import xyz.quaver.hitomi.getReferer
|
||||
import xyz.quaver.hitomi.urlFromUrlFromHash
|
||||
import xyz.quaver.hiyobi.cookie
|
||||
import xyz.quaver.hiyobi.createImgList
|
||||
import xyz.quaver.hiyobi.user_agent
|
||||
import xyz.quaver.pupil.Pupil
|
||||
import xyz.quaver.pupil.R
|
||||
@@ -83,7 +85,7 @@ class GalleryDownloader(
|
||||
onNotifyChangedHandler?.invoke(value)
|
||||
}
|
||||
|
||||
private val reader: Deferred<Reader>?
|
||||
private val reader: Deferred<Reader?>?
|
||||
private var downloadJob: Job? = null
|
||||
|
||||
private lateinit var notificationBuilder: NotificationCompat.Builder
|
||||
@@ -121,11 +123,8 @@ class GalleryDownloader(
|
||||
if (cache.exists()) {
|
||||
val cached = json.parse(serializer, cache.readText())
|
||||
|
||||
if (cached.readerItems.isNotEmpty()) {
|
||||
useHiyobi = when {
|
||||
cached.readerItems[0].url.contains("hitomi.la") -> false
|
||||
else -> true
|
||||
}
|
||||
if (cached.galleryInfo.isNotEmpty()) {
|
||||
useHiyobi = availableInHiyobi(galleryID)
|
||||
|
||||
onReaderLoadedHandler?.invoke(cached)
|
||||
|
||||
@@ -148,7 +147,7 @@ class GalleryDownloader(
|
||||
}
|
||||
}
|
||||
|
||||
if (reader.readerItems.isNotEmpty()) {
|
||||
if (reader.galleryInfo.isNotEmpty()) {
|
||||
//Save cache
|
||||
if (cache.parentFile?.exists() == false)
|
||||
cache.parentFile!!.mkdirs()
|
||||
@@ -159,7 +158,8 @@ class GalleryDownloader(
|
||||
reader
|
||||
} catch (e: Exception) {
|
||||
Crashlytics.logException(e)
|
||||
Reader("", listOf())
|
||||
onErrorHandler?.invoke(e)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,29 +168,36 @@ class GalleryDownloader(
|
||||
|
||||
fun start() {
|
||||
downloadJob = CoroutineScope(Dispatchers.Default).launch {
|
||||
val reader = reader!!.await()
|
||||
val reader = reader!!.await() ?: return@launch
|
||||
|
||||
notificationBuilder.setContentTitle(reader.title)
|
||||
|
||||
if (reader.readerItems.isEmpty()) {
|
||||
onErrorHandler?.invoke(IOException(getString(R.string.unable_to_connect)))
|
||||
return@launch
|
||||
}
|
||||
|
||||
val list = ArrayList<String>()
|
||||
|
||||
onReaderLoadedHandler?.invoke(reader)
|
||||
|
||||
notificationBuilder
|
||||
.setProgress(reader.readerItems.size, 0, false)
|
||||
.setContentText("0/${reader.readerItems.size}")
|
||||
.setProgress(reader.galleryInfo.size, 0, false)
|
||||
.setContentText("0/${reader.galleryInfo.size}")
|
||||
|
||||
reader.readerItems.chunked(4).forEachIndexed { chunkIndex, chunked ->
|
||||
chunked.mapIndexed { i, it ->
|
||||
reader.galleryInfo.chunked(4).forEachIndexed { chunkIndex, chunked ->
|
||||
chunked.mapIndexed { i, galleryInfo ->
|
||||
val index = chunkIndex*4+i
|
||||
|
||||
async(Dispatchers.IO) {
|
||||
val url = if (it.galleryInfo?.haswebp == 1) webpUrlFromUrl(it.url) else it.url
|
||||
val url = when(useHiyobi) {
|
||||
true -> createImgList(galleryID, reader)[index].path
|
||||
false -> when (galleryInfo.haswebp) {
|
||||
1 -> webpUrlFromUrl(
|
||||
urlFromUrlFromHash(
|
||||
galleryID,
|
||||
galleryInfo,
|
||||
true
|
||||
)
|
||||
)
|
||||
else -> urlFromUrlFromHash(galleryID, galleryInfo)
|
||||
}
|
||||
}
|
||||
|
||||
val name = "$index".padStart(4, '0')
|
||||
val ext = url.split('.').last()
|
||||
@@ -234,8 +241,8 @@ class GalleryDownloader(
|
||||
onProgressHandler?.invoke(index)
|
||||
|
||||
notificationBuilder
|
||||
.setProgress(reader.readerItems.size, index, false)
|
||||
.setContentText("$index/${reader.readerItems.size}")
|
||||
.setProgress(reader.galleryInfo.size, index, false)
|
||||
.setContentText("$index/${reader.galleryInfo.size}")
|
||||
|
||||
if (download)
|
||||
notificationManager.notify(galleryID, notificationBuilder.build())
|
||||
|
||||
@@ -21,8 +21,6 @@ package xyz.quaver.pupil.util
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.provider.MediaStore
|
||||
import androidx.core.content.ContextCompat
|
||||
import java.io.File
|
||||
|
||||
fun getCachedGallery(context: Context, galleryID: Int): File {
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
android:padding="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/gallery_thumbnail"
|
||||
android:id="@+id/gallery_cover"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
@@ -55,7 +55,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintLeft_toRightOf="@id/gallery_thumbnail"
|
||||
app:layout_constraintLeft_toRightOf="@id/gallery_cover"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"/>
|
||||
@@ -66,7 +66,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/gallery_title"
|
||||
app:layout_constraintLeft_toRightOf="@id/gallery_thumbnail"
|
||||
app:layout_constraintLeft_toRightOf="@id/gallery_cover"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"/>
|
||||
@@ -83,7 +83,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toRightOf="@id/gallery_thumbnail"
|
||||
app:layout_constraintLeft_toRightOf="@id/gallery_cover"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"/>
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
android:id="@+id/galleryblock_download"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:minWidth="70dp"
|
||||
android:padding="8dp"
|
||||
android:gravity="center"
|
||||
android:background="@android:color/holo_blue_dark"
|
||||
@@ -55,6 +56,7 @@
|
||||
android:id="@+id/galleryblock_delete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:minWidth="70dp"
|
||||
android:padding="8dp"
|
||||
android:gravity="center"
|
||||
android:background="@android:color/holo_red_dark"
|
||||
|
||||
@@ -5,16 +5,14 @@ buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven {
|
||||
url 'https://maven.fabric.io/public'
|
||||
}
|
||||
maven { url 'https://maven.fabric.io/public' }
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.0'
|
||||
classpath 'com.android.tools.build:gradle:3.5.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
|
||||
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||
classpath 'com.google.gms:google-services:4.3.1'
|
||||
classpath 'com.google.gms:google-services:4.3.2'
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
classpath 'io.fabric.tools:gradle:1.29.0'
|
||||
|
||||
26
libpupil/src/main/java/xyz/quaver/Utils.kt
Normal file
26
libpupil/src/main/java/xyz/quaver/Utils.kt
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2019 tom5079
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package xyz.quaver
|
||||
|
||||
fun availableInHiyobi(galleryID: Int) : Boolean {
|
||||
return try {
|
||||
xyz.quaver.hiyobi.getReader(galleryID)
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -16,11 +16,25 @@
|
||||
|
||||
package xyz.quaver.hitomi
|
||||
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
import kotlinx.serialization.list
|
||||
import java.net.URL
|
||||
|
||||
const val protocol = "https:"
|
||||
|
||||
fun getGalleryInfo(galleryID: Int): List<GalleryInfo> {
|
||||
return Json(JsonConfiguration.Stable).parse(
|
||||
GalleryInfo.serializer().list,
|
||||
Regex("""\[.+]""").find(
|
||||
URL("$protocol//$domain/galleries/$galleryID.js").readText()
|
||||
)?.value ?: "[]"
|
||||
)
|
||||
}
|
||||
|
||||
//common.js
|
||||
var adapose = false
|
||||
const val numberOfFrontends = 2
|
||||
const val numberOfFrontends = 3
|
||||
const val domain = "ltn.hitomi.la"
|
||||
const val galleryblockdir = "galleryblock"
|
||||
const val nozomiextension = ".nozomi"
|
||||
@@ -37,20 +51,22 @@ fun subdomainFromGalleryID(g: Int) : String {
|
||||
fun subdomainFromURL(url: String, base: String? = null) : String {
|
||||
var retval = "a"
|
||||
|
||||
if (base != null)
|
||||
if (!base.isNullOrEmpty())
|
||||
retval = base
|
||||
|
||||
val r = Regex("""/\d*(\d)/""")
|
||||
val m = r.find(url)
|
||||
val r = Regex("""/galleries/\d*(\d)/""")
|
||||
var m = r.find(url)
|
||||
var b = 10
|
||||
|
||||
m ?: return retval
|
||||
if (m == null) {
|
||||
b = 16
|
||||
val r2 = Regex("""/images/[0-9a-f]/([0-9a-f]{2})/""")
|
||||
m = r2.find(url)
|
||||
if (m == null)
|
||||
return retval
|
||||
}
|
||||
|
||||
var g = m.groups[1]!!.value.toIntOrNull()
|
||||
|
||||
g ?: return retval
|
||||
|
||||
if (g == 1)
|
||||
g = 0
|
||||
val g = m.groupValues[1].toIntOrNull(b) ?: return retval
|
||||
|
||||
retval = subdomainFromGalleryID(g) + retval
|
||||
|
||||
@@ -58,5 +74,22 @@ fun subdomainFromURL(url: String, base: String? = null) : String {
|
||||
}
|
||||
|
||||
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? {
|
||||
return when {
|
||||
hash?.length ?: 0 < 3 -> hash
|
||||
else -> hash!!.replace(Regex("^.*(..)(.)$"), "$2/$1/$hash")
|
||||
}
|
||||
}
|
||||
|
||||
fun urlFromHash(galleryID: Int, image: GalleryInfo, oldMethod: Boolean) : String {
|
||||
return when {
|
||||
oldMethod or image.hash.isNullOrEmpty() -> "$protocol//a.hitomi.la/galleries/$galleryID/${image.name}"
|
||||
else -> "$protocol//a.hitomi.la/images/${fullPathFromHash(image.hash)}.${image.name.split('.').last()}"
|
||||
}
|
||||
}
|
||||
|
||||
fun urlFromUrlFromHash(galleryID: Int, image: GalleryInfo, oldMethod: Boolean = false) =
|
||||
urlFromURL(urlFromHash(galleryID, image, oldMethod))
|
||||
@@ -17,7 +17,6 @@
|
||||
package xyz.quaver.hitomi
|
||||
|
||||
import org.jsoup.Jsoup
|
||||
import java.net.URL
|
||||
import java.net.URLDecoder
|
||||
|
||||
data class Gallery(
|
||||
@@ -35,7 +34,8 @@ data class Gallery(
|
||||
val thumbnails: List<String>
|
||||
)
|
||||
fun getGallery(galleryID: Int) : Gallery {
|
||||
val url = "https://hitomi.la/galleries/$galleryID.html"
|
||||
val url = Jsoup.connect("https://hitomi.la/galleries/$galleryID.html").get()
|
||||
.select("a").attr("href")
|
||||
|
||||
val doc = Jsoup.connect(url).get()
|
||||
|
||||
@@ -46,7 +46,7 @@ fun getGallery(galleryID: Int) : Gallery {
|
||||
}.toList()
|
||||
|
||||
val langList = doc.select("#lang-list a").map {
|
||||
Pair(it.text(), it.attr("href").replace(".html", ""))
|
||||
Pair(it.text(), "$protocol//hitomi.la${it.attr("href")}")
|
||||
}
|
||||
|
||||
val cover = protocol + doc.selectFirst(".cover img").attr("src")
|
||||
@@ -68,11 +68,9 @@ fun getGallery(galleryID: Int) : Gallery {
|
||||
href.slice(5 until href.indexOf('-'))
|
||||
}
|
||||
|
||||
val thumbnails = Regex("'(//tn.hitomi.la/smalltn/\\d+/.+)',")
|
||||
.findAll(doc.select("script").last().html())
|
||||
.map {
|
||||
protocol + it.groups[1]!!.value
|
||||
}.toList()
|
||||
val thumbnails = getGalleryInfo(galleryID).map {
|
||||
"$protocol//tn.hitomi.la/smalltn/$galleryID/${it.name}.jpg"
|
||||
}
|
||||
|
||||
return Gallery(related, langList, cover, title, artists, groups, type, language, series, characters, tags, thumbnails)
|
||||
}
|
||||
@@ -18,7 +18,6 @@ package xyz.quaver.hitomi
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.jsoup.Jsoup
|
||||
import sun.rmi.runtime.Log
|
||||
import java.net.URL
|
||||
import java.net.URLDecoder
|
||||
import java.nio.ByteBuffer
|
||||
@@ -69,6 +68,7 @@ fun fetchNozomi(area: String? = null, tag: String = "index", language: String =
|
||||
@Serializable
|
||||
data class GalleryBlock(
|
||||
val id: Int,
|
||||
val galleryUrl: String,
|
||||
val thumbnails: List<String>,
|
||||
val title: String,
|
||||
val artists: List<String>,
|
||||
@@ -83,6 +83,8 @@ fun getGalleryBlock(galleryID: Int) : GalleryBlock? {
|
||||
try {
|
||||
val doc = Jsoup.connect(url).get()
|
||||
|
||||
val galleryUrl = doc.selectFirst(".lillie").attr("href")
|
||||
|
||||
val thumbnails = doc.select("img").map { protocol + it.attr("data-src") }
|
||||
|
||||
val title = doc.selectFirst("h1.lillie > a").text()
|
||||
@@ -100,7 +102,7 @@ fun getGalleryBlock(galleryID: Int) : GalleryBlock? {
|
||||
href.slice(5 until href.indexOf("-all"))
|
||||
}
|
||||
|
||||
return GalleryBlock(galleryID, thumbnails, title, artists, series, type, language, relatedTags)
|
||||
return GalleryBlock(galleryID, galleryUrl, thumbnails, title, artists, series, type, language, relatedTags)
|
||||
} catch (e: Exception) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -17,72 +17,28 @@
|
||||
package xyz.quaver.hitomi
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
import kotlinx.serialization.list
|
||||
import org.jsoup.Jsoup
|
||||
import xyz.quaver.hiyobi.HiyobiReader
|
||||
import java.net.URL
|
||||
|
||||
fun getReferer(galleryID: Int) = "https://hitomi.la/reader/$galleryID.html"
|
||||
fun webpUrlFromUrl(url: String) = url.replace("/galleries/", "/webp/") + ".webp"
|
||||
|
||||
fun webpReaderFromReader(reader: Reader) : Reader {
|
||||
if (reader is HiyobiReader)
|
||||
return reader
|
||||
|
||||
return Reader(reader.title, reader.readerItems.map {
|
||||
ReaderItem(
|
||||
if (it.galleryInfo?.haswebp == 1) webpUrlFromUrl(it.url) else it.url,
|
||||
it.galleryInfo
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class GalleryInfo(
|
||||
val width: Int,
|
||||
val hash: String?,
|
||||
val haswebp: Int,
|
||||
val name: String,
|
||||
val height: Int
|
||||
)
|
||||
@Serializable
|
||||
data class ReaderItem(
|
||||
val url: String,
|
||||
val galleryInfo: GalleryInfo?
|
||||
)
|
||||
|
||||
@Serializable
|
||||
open class Reader(val title: String, val readerItems: List<ReaderItem>)
|
||||
open class Reader(val title: String, val galleryInfo: List<GalleryInfo>)
|
||||
|
||||
//Set header `Referer` to reader url to avoid 403 error
|
||||
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()
|
||||
|
||||
val title = doc.title()
|
||||
|
||||
val images = doc.select(".img-url").map {
|
||||
protocol + urlFromURL(it.text())
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
return Reader(title, (images zip galleryInfo).map {
|
||||
ReaderItem(it.first, it.second)
|
||||
})
|
||||
return Reader(doc.title(), getGalleryInfo(galleryID))
|
||||
}
|
||||
@@ -18,18 +18,17 @@ package xyz.quaver.hiyobi
|
||||
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
import kotlinx.serialization.json.content
|
||||
import kotlinx.serialization.list
|
||||
import org.jsoup.Jsoup
|
||||
import xyz.quaver.hitomi.GalleryInfo
|
||||
import xyz.quaver.hitomi.Reader
|
||||
import xyz.quaver.hitomi.ReaderItem
|
||||
import xyz.quaver.hitomi.protocol
|
||||
import java.net.URL
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
|
||||
const val hiyobi = "xn--9w3b15m8vo.asia"
|
||||
const val user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36"
|
||||
|
||||
class HiyobiReader(title: String, readerItems: List<ReaderItem>) : Reader(title, readerItems)
|
||||
|
||||
var cookie: String = ""
|
||||
get() {
|
||||
if (field.isEmpty())
|
||||
@@ -38,6 +37,12 @@ var cookie: String = ""
|
||||
return field
|
||||
}
|
||||
|
||||
data class Images(
|
||||
val path: String,
|
||||
val no: Int,
|
||||
val name: String
|
||||
)
|
||||
|
||||
fun renewCookie() : String {
|
||||
val url = "https://$hiyobi/"
|
||||
|
||||
@@ -59,7 +64,8 @@ fun getReader(galleryID: Int) : Reader {
|
||||
|
||||
val title = Jsoup.connect(reader).get().title()
|
||||
|
||||
val json = Json(JsonConfiguration.Stable).parseJson(
|
||||
val galleryInfo = Json(JsonConfiguration.Stable).parse(
|
||||
GalleryInfo.serializer().list,
|
||||
with(URL(url).openConnection() as HttpsURLConnection) {
|
||||
setRequestProperty("User-Agent", user_agent)
|
||||
setRequestProperty("Cookie", cookie)
|
||||
@@ -70,8 +76,8 @@ fun getReader(galleryID: Int) : Reader {
|
||||
}
|
||||
)
|
||||
|
||||
return Reader(title, json.jsonArray.map {
|
||||
val name = it.jsonObject["name"]!!.content
|
||||
ReaderItem("https://$hiyobi/data/$galleryID/$name", null)
|
||||
})
|
||||
}
|
||||
return Reader(title, galleryInfo)
|
||||
}
|
||||
|
||||
fun createImgList(galleryID: Int, reader: Reader) =
|
||||
reader.galleryInfo.map { Images("$protocol//$hiyobi/data/$galleryID/${it.name}", galleryID, it.name) }
|
||||
@@ -18,12 +18,16 @@
|
||||
|
||||
package xyz.quaver.hitomi
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class UnitTest {
|
||||
@Test
|
||||
fun test() {
|
||||
|
||||
assertEquals(
|
||||
"6/2d/c26014fc6153ef717932d85f4d26c75195560fb2ce1da60b431ef376501642d6",
|
||||
fullPathFromHash("c26014fc6153ef717932d85f4d26c75195560fb2ce1da60b431ef376501642d6")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -63,7 +67,7 @@ class UnitTest {
|
||||
|
||||
@Test
|
||||
fun test_getGallery() {
|
||||
val gallery = getGallery(1405267)
|
||||
val gallery = getGallery(1510566)
|
||||
|
||||
print(gallery)
|
||||
}
|
||||
@@ -77,6 +81,15 @@ class UnitTest {
|
||||
|
||||
@Test
|
||||
fun test_hiyobi() {
|
||||
xyz.quaver.hiyobi.getReader(1510567)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_urlFromUrlFromHash() {
|
||||
val url = urlFromUrlFromHash(1510702, GalleryInfo(
|
||||
210, "56e9e1b8bb72194777ed93fee11b06070b905039dd11348b070bcf1793aaed7b", 1, "6.jpg", 300
|
||||
))
|
||||
|
||||
print(url)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user