Add extension for LANraragi (#1383)

* Add extension for LANraragi

LANraragi project page: https://github.com/Difegue/LANraragi

* Drop tag exclusion support and improve tag search perfomance a bit

* LANraragi: API fixes as of 0.6.7

Image routing was 404ing in Mojolicious due to preceding / resulting in //api/page
Manga parsing was failing with "no fun mode" due to wrong URL parameter for key

* Update to newer API

* Fix strange bug on last page

* Increment version code

* Reset increment back to 1 & Instantiate Gson once only

Co-authored-by: RePod <RePod@users.noreply.github.com>
This commit is contained in:
Josef Christian Halim 2020-02-01 23:06:38 +07:00 committed by GitHub
parent 731df4ccdd
commit 0f3f79eebd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 299 additions and 0 deletions

View File

@ -0,0 +1,21 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
appName = 'Tachiyomi: LANraragi'
pkgNameSuffix = 'all.lanraragi'
extClass = '.LANraragi'
extVersionCode = 1
libVersion = '1.2'
}
dependencies {
compileOnly project(':preference-stub')
compileOnly 'com.github.inorichi.injekt:injekt-core:65b0440'
compileOnly 'com.google.code.gson:gson:2.8.2'
compileOnly 'com.github.salomonbrys.kotson:kotson:2.5.0'
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -0,0 +1,257 @@
package eu.kanade.tachiyomi.extension.all.lanraragi
import android.app.Application
import android.content.SharedPreferences
import android.net.Uri
import android.support.v7.preference.EditTextPreference
import android.support.v7.preference.PreferenceScreen
import com.github.salomonbrys.kotson.fromJson
import com.google.gson.Gson
import eu.kanade.tachiyomi.extension.all.lanraragi.model.ArchivePage
import eu.kanade.tachiyomi.extension.all.lanraragi.model.ArchiveSearchResult
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.online.HttpSource
import okhttp3.Request
import okhttp3.Response
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
open class LANraragi : ConfigurableSource, HttpSource() {
override val baseUrl: String
get() = preferences.getString("hostname", "http://127.0.0.1:3000")!!
override val lang = "all"
override val name = "LANraragi"
override val supportsLatest = true
private val apiKey: String
get() = preferences.getString("apiKey", "")!!
private val gson: Gson = Gson()
override fun mangaDetailsParse(response: Response): SManga {
val id = getId(response)
return SManga.create().apply {
thumbnail_url = getThumbnailUri(id)
}
}
override fun chapterListParse(response: Response): List<SChapter> {
val id = getId(response)
val uri = getApiUriBuilder("/api/extract")
uri.appendQueryParameter("id", id)
return listOf(
SChapter.create().apply {
val uriBuild = uri.build()
url = "${uriBuild.encodedPath}?${uriBuild.encodedQuery}"
chapter_number = 1F
name = "Chapter"
})
}
override fun pageListParse(response: Response): List<Page> {
val archivePage = gson.fromJson<ArchivePage>(response.body()!!.string())
return archivePage.pages.mapIndexed { index, url ->
val uri = Uri.parse("${baseUrl}${url.trimStart('.')}")
Page(index, uri.toString(), uri.toString(), uri)
}
}
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("imageUrlParse is unused")
override fun popularMangaRequest(page: Int): Request {
return searchMangaRequest(page, "", FilterList())
}
override fun popularMangaParse(response: Response): MangasPage {
return searchMangaParse(response)
}
override fun latestUpdatesRequest(page: Int): Request {
return searchMangaRequest(page, "", FilterList())
}
override fun latestUpdatesParse(response: Response): MangasPage {
return searchMangaParse(response)
}
private var lastResultCount: Int = 100
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val uri = getApiUriBuilder("/api/search")
uri.appendQueryParameter("start", ((page - 1) * lastResultCount).toString())
if (query.isNotEmpty()) {
uri.appendQueryParameter("filter", query)
}
return GET(uri.toString())
}
override fun searchMangaParse(response: Response): MangasPage {
val jsonResult = gson.fromJson<ArchiveSearchResult>(response.body()!!.string())
val currentStart = getStart(response)
lastResultCount = jsonResult.data.size
return MangasPage(
jsonResult.data.map {
SManga.create().apply {
url = "/reader?id=${it.arcid}"
title = it.title
thumbnail_url = getThumbnailUri(it.arcid)
genre = it.tags
artist = getArtist(it.tags)
author = artist
}
}, currentStart + jsonResult.data.size < jsonResult.recordsFiltered)
}
// Preferences
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val hostnamePref = EditTextPreference(screen.context).apply {
key = "Hostname"
title = "Hostname"
text = baseUrl
summary = baseUrl
dialogTitle = "Hostname"
setOnPreferenceChangeListener { _, newValue ->
var hostname = newValue as String
if (!hostname.startsWith("http://") && !hostname.startsWith("https://")) {
hostname = "http://$hostname"
}
this.apply {
text = hostname
summary = hostname
}
preferences.edit().putString("hostname", hostname).commit()
}
}
val apiKeyPref = EditTextPreference(screen.context).apply {
key = "API Key"
title = "API Key"
text = apiKey
summary = apiKey
dialogTitle = "API Key"
setOnPreferenceChangeListener { _, newValue ->
val apiKey = newValue as String
this.apply {
text = apiKey
summary = apiKey
}
preferences.edit().putString("apiKey", newValue).commit()
}
}
screen.addPreference(hostnamePref)
screen.addPreference(apiKeyPref)
}
override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) {
val hostnamePref = androidx.preference.EditTextPreference(screen.context).apply {
key = "Hostname"
title = "Hostname"
text = baseUrl
summary = baseUrl
dialogTitle = "Hostname"
setOnPreferenceChangeListener { _, newValue ->
var hostname = newValue as String
if (!hostname.startsWith("http://") && !hostname.startsWith("https://")) {
hostname = "http://$hostname"
}
this.apply {
text = hostname
summary = hostname
}
preferences.edit().putString("hostname", hostname).commit()
}
}
val apiKeyPref = androidx.preference.EditTextPreference(screen.context).apply {
key = "API Key"
title = "API Key"
text = apiKey
summary = apiKey
dialogTitle = "API Key"
setOnPreferenceChangeListener { _, newValue ->
val apiKey = newValue as String
this.apply {
text = apiKey
summary = apiKey
}
preferences.edit().putString("apiKey", newValue).commit()
}
}
screen.addPreference(hostnamePref)
screen.addPreference(apiKeyPref)
}
// Helper
private fun getApiUriBuilder(path: String): Uri.Builder {
val uri = Uri.parse("$baseUrl$path").buildUpon()
if (apiKey.isNotEmpty()) {
uri.appendQueryParameter("key", apiKey)
}
return uri
}
private fun getThumbnailUri(id: String): String {
val uri = getApiUriBuilder("/api/thumbnail")
uri.appendQueryParameter("id", id)
return uri.toString()
}
private fun getTopResponse(response: Response): Response {
return if (response.priorResponse() == null) response else getTopResponse(response.priorResponse()!!)
}
private fun getId(response: Response): String {
return getTopResponse(response).request().url().queryParameter("id").toString()
}
private fun getStart(response: Response): Int {
return getTopResponse(response).request().url().queryParameter("start")!!.toInt()
}
private fun getArtist(tags: String): String {
tags.split(',').forEach {
if (it.contains(':')) {
val temp = it.trim().split(':')
if (temp[0].equals("artist", true)) return temp[1]
}
}
return "N/A"
}
}

View File

@ -0,0 +1,8 @@
package eu.kanade.tachiyomi.extension.all.lanraragi.model
data class Archive(
val arcid: String,
val isnew: String,
val tags: String,
val title: String
)

View File

@ -0,0 +1,5 @@
package eu.kanade.tachiyomi.extension.all.lanraragi.model
data class ArchivePage(
val pages: List<String>
)

View File

@ -0,0 +1,8 @@
package eu.kanade.tachiyomi.extension.all.lanraragi.model
data class ArchiveSearchResult(
val data: List<Archive>,
val draw: Int,
val recordsFiltered: Int,
val recordsTotal: Int
)