Add "public url" field to manga
This commit is contained in:
@@ -81,6 +81,7 @@ class BackupRepository(private val db: MangaDatabase) {
|
||||
jo.put("title", title)
|
||||
jo.put("alt_title", altTitle)
|
||||
jo.put("url", url)
|
||||
jo.put("public_url", publicUrl)
|
||||
jo.put("rating", rating)
|
||||
jo.put("cover_url", coverUrl)
|
||||
jo.put("large_cover_url", largeCoverUrl)
|
||||
|
||||
@@ -70,6 +70,7 @@ class RestoreRepository(private val db: MangaDatabase) {
|
||||
title = json.getString("title"),
|
||||
altTitle = json.getStringOrNull("alt_title"),
|
||||
url = json.getString("url"),
|
||||
publicUrl = json.getStringOrNull("public_url").orEmpty(),
|
||||
rating = json.getDouble("rating").toFloat(),
|
||||
coverUrl = json.getString("cover_url"),
|
||||
largeCoverUrl = json.getStringOrNull("large_cover_url"),
|
||||
|
||||
@@ -17,7 +17,8 @@ val databaseModule
|
||||
Migration2To3(),
|
||||
Migration3To4(),
|
||||
Migration4To5(),
|
||||
Migration5To6()
|
||||
Migration5To6(),
|
||||
Migration6To7()
|
||||
).addCallback(
|
||||
DatabasePrePopulateCallback(androidContext().resources)
|
||||
).build()
|
||||
|
||||
@@ -16,7 +16,7 @@ import org.koitharu.kotatsu.history.data.HistoryEntity
|
||||
MangaEntity::class, TagEntity::class, HistoryEntity::class, MangaTagsEntity::class,
|
||||
FavouriteCategoryEntity::class, FavouriteEntity::class, MangaPrefsEntity::class,
|
||||
TrackEntity::class, TrackLogEntity::class
|
||||
], version = 6
|
||||
], version = 7
|
||||
)
|
||||
abstract class MangaDatabase : RoomDatabase() {
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ data class MangaEntity(
|
||||
@ColumnInfo(name = "title") val title: String,
|
||||
@ColumnInfo(name = "alt_title") val altTitle: String? = null,
|
||||
@ColumnInfo(name = "url") val url: String,
|
||||
@ColumnInfo(name = "public_url") val publicUrl: String,
|
||||
@ColumnInfo(name = "rating") val rating: Float = Manga.NO_RATING, //normalized value [0..1] or -1
|
||||
@ColumnInfo(name = "cover_url") val coverUrl: String,
|
||||
@ColumnInfo(name = "large_cover_url") val largeCoverUrl: String? = null,
|
||||
@@ -30,6 +31,7 @@ data class MangaEntity(
|
||||
state = this.state?.let { MangaState.valueOf(it) },
|
||||
rating = this.rating,
|
||||
url = this.url,
|
||||
publicUrl = this.publicUrl,
|
||||
coverUrl = this.coverUrl,
|
||||
largeCoverUrl = this.largeCoverUrl,
|
||||
author = this.author,
|
||||
@@ -42,6 +44,7 @@ data class MangaEntity(
|
||||
fun from(manga: Manga) = MangaEntity(
|
||||
id = manga.id,
|
||||
url = manga.url,
|
||||
publicUrl = manga.publicUrl,
|
||||
source = manga.source.name,
|
||||
largeCoverUrl = manga.largeCoverUrl,
|
||||
coverUrl = manga.coverUrl,
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.koitharu.kotatsu.core.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration6To7 : Migration(6, 7) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE manga ADD COLUMN public_url TEXT NOT NULL DEFAULT ''")
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,8 @@ data class Manga(
|
||||
val id: Long,
|
||||
val title: String,
|
||||
val altTitle: String? = null,
|
||||
val url: String,
|
||||
val url: String, // relative url for internal use
|
||||
val publicUrl: String,
|
||||
val rating: Float = NO_RATING, //normalized value [0..1] or -1
|
||||
val coverUrl: String,
|
||||
val largeCoverUrl: String? = null,
|
||||
|
||||
@@ -31,7 +31,7 @@ abstract class RemoteMangaRepository(
|
||||
|
||||
protected fun getDomain() = conf.getDomain(defaultDomain)
|
||||
|
||||
protected fun String.withDomain() = when {
|
||||
protected fun String.withDomain(subdomain: String? = null) = when {
|
||||
this.startsWith("//") -> buildString {
|
||||
append("http")
|
||||
if (conf.isUseSsl(true)) {
|
||||
@@ -46,6 +46,10 @@ abstract class RemoteMangaRepository(
|
||||
append('s')
|
||||
}
|
||||
append("://")
|
||||
if (subdomain != null) {
|
||||
append(subdomain)
|
||||
append('.')
|
||||
}
|
||||
append(conf.getDomain(defaultDomain))
|
||||
append(this@withDomain)
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
|
||||
Manga(
|
||||
id = generateUid(href),
|
||||
url = href,
|
||||
publicUrl = href.inContextOf(a),
|
||||
altTitle = a.attr("title"),
|
||||
title = a.text().substringAfterLast('(').substringBeforeLast(')'),
|
||||
author = row.getElementsByAttributeValueStarting(
|
||||
|
||||
@@ -57,6 +57,7 @@ class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
|
||||
val id = jo.getLong("id")
|
||||
list += Manga(
|
||||
url = "/manga/api/$id",
|
||||
publicUrl = jo.getString("url"),
|
||||
source = MangaSource.DESUME,
|
||||
title = jo.getString("russian"),
|
||||
altTitle = jo.getString("name"),
|
||||
@@ -89,6 +90,7 @@ class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
|
||||
source = manga.source
|
||||
)
|
||||
},
|
||||
publicUrl = json.getString("url"),
|
||||
description = json.getString("description"),
|
||||
chapters = chaptersList.mapIndexed { i, it ->
|
||||
val chid = it.getLong("id")
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package org.koitharu.kotatsu.core.parser.site
|
||||
|
||||
import androidx.collection.arraySetOf
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import org.koitharu.kotatsu.base.domain.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.core.exceptions.ParseException
|
||||
import org.koitharu.kotatsu.core.model.*
|
||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||
import org.koitharu.kotatsu.core.prefs.SourceSettings
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
import java.util.*
|
||||
|
||||
@@ -70,6 +68,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
||||
Manga(
|
||||
id = generateUid(relUrl),
|
||||
url = relUrl,
|
||||
publicUrl = href,
|
||||
title = title,
|
||||
altTitle = descDiv.selectFirst("h4")?.text(),
|
||||
coverUrl = imgDiv.selectFirst("img.lazy")?.attr("data-original").orEmpty(),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.koitharu.kotatsu.core.parser.site
|
||||
|
||||
import androidx.collection.ArraySet
|
||||
import androidx.collection.arraySetOf
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import org.koitharu.kotatsu.base.domain.MangaLoaderContext
|
||||
@@ -9,7 +8,6 @@ import org.koitharu.kotatsu.core.exceptions.AuthRequiredException
|
||||
import org.koitharu.kotatsu.core.exceptions.ParseException
|
||||
import org.koitharu.kotatsu.core.model.*
|
||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||
import org.koitharu.kotatsu.core.prefs.SourceSettings
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
@@ -65,6 +63,7 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
||||
author = null,
|
||||
rating = Manga.NO_RATING,
|
||||
url = href,
|
||||
publicUrl = href.inContextOf(a),
|
||||
tags = emptySet(),
|
||||
state = null,
|
||||
source = source
|
||||
@@ -218,11 +217,12 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
||||
.parseJsonArray()
|
||||
return json.map { jo ->
|
||||
val slug = jo.getString("slug")
|
||||
val url = "https://$domain/$slug"
|
||||
val url = "/$slug"
|
||||
val covers = jo.getJSONObject("covers")
|
||||
Manga(
|
||||
id = generateUid(slug),
|
||||
id = generateUid(url),
|
||||
url = url,
|
||||
publicUrl = "https://$domain/$slug",
|
||||
title = jo.getString("rus_name"),
|
||||
altTitle = jo.getString("name"),
|
||||
author = null,
|
||||
|
||||
@@ -77,7 +77,8 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
||||
source = MangaSource.MANGATOWN
|
||||
)
|
||||
}.orEmpty(),
|
||||
url = href
|
||||
url = href,
|
||||
publicUrl = href.inContextOf(a)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package org.koitharu.kotatsu.core.parser.site
|
||||
|
||||
import androidx.collection.arraySetOf
|
||||
import org.koitharu.kotatsu.base.domain.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.core.exceptions.ParseException
|
||||
import org.koitharu.kotatsu.core.model.*
|
||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||
import org.koitharu.kotatsu.core.prefs.SourceSettings
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
import java.util.*
|
||||
|
||||
@@ -50,6 +48,7 @@ class MangareadRepository(
|
||||
Manga(
|
||||
id = generateUid(href),
|
||||
url = href,
|
||||
publicUrl = href.inContextOf(div),
|
||||
coverUrl = div.selectFirst("img").attr("data-srcset")
|
||||
.split(',').firstOrNull()?.substringBeforeLast(' ').orEmpty(),
|
||||
title = summary.selectFirst("h3").text(),
|
||||
|
||||
@@ -58,6 +58,7 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
|
||||
Manga(
|
||||
id = generateUid(url),
|
||||
url = url,
|
||||
publicUrl = "https://$domain$url",
|
||||
title = jo.getString("rus_name"),
|
||||
altTitle = jo.getString("en_name"),
|
||||
rating = jo.getString("avg_rating").toFloatOrNull()?.div(10f) ?: Manga.NO_RATING,
|
||||
@@ -112,7 +113,7 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
|
||||
val name = jo.getString("name")
|
||||
MangaChapter(
|
||||
id = generateUid(id),
|
||||
url = "https://api.$domain/api/titles/chapters/$id/",
|
||||
url = "/api/titles/chapters/$id/",
|
||||
number = chapters.length() - i,
|
||||
name = buildString {
|
||||
append("Глава ")
|
||||
@@ -130,7 +131,7 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
|
||||
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
val referer = "https://${getDomain()}/"
|
||||
val content = loaderContext.httpGet(chapter.url.withDomain()).parseJson()
|
||||
val content = loaderContext.httpGet(chapter.url.withDomain(subdomain = "api")).parseJson()
|
||||
.getJSONObject("content").getJSONArray("pages")
|
||||
val pages = ArrayList<MangaPage>(content.length())
|
||||
for (i in 0 until content.length()) {
|
||||
|
||||
@@ -19,6 +19,7 @@ class MangaIndex(source: String?) {
|
||||
json.put("title", manga.title)
|
||||
json.put("title_alt", manga.altTitle)
|
||||
json.put("url", manga.url)
|
||||
json.put("public_url", manga.publicUrl)
|
||||
json.put("cover", manga.coverUrl)
|
||||
json.put("description", manga.description)
|
||||
json.put("rating", manga.rating)
|
||||
@@ -46,6 +47,7 @@ class MangaIndex(source: String?) {
|
||||
title = json.getString("title"),
|
||||
altTitle = json.getStringOrNull("title_alt"),
|
||||
url = json.getString("url"),
|
||||
publicUrl = json.getStringOrNull("public_url").orEmpty(),
|
||||
source = source,
|
||||
rating = json.getDouble("rating").toFloat(),
|
||||
coverUrl = json.getString("cover"),
|
||||
|
||||
@@ -112,6 +112,7 @@ class LocalMangaRepository(private val context: Context) : MangaRepository {
|
||||
id = file.absolutePath.longHashCode(),
|
||||
title = title,
|
||||
url = fileUri,
|
||||
publicUrl = fileUri,
|
||||
source = MangaSource.LOCAL,
|
||||
coverUrl = zipUri(file, findFirstEntry(zip.entries(), isImage = true)?.name.orEmpty()),
|
||||
chapters = chapters.sortedWith(AlphanumComparator()).mapIndexed { i, s ->
|
||||
|
||||
@@ -40,6 +40,11 @@ class RemoteRepositoryTest(source: MangaSource) : KoinTest {
|
||||
AssertX.assertUrlRelative("Url is not relative", item.url)
|
||||
AssertX.assertUrlAbsolute("Url is not absolute", item.coverUrl)
|
||||
AssertX.assertContentType("Bad cover at ${item.url}", item.coverUrl, "image/*")
|
||||
AssertX.assertContentType(
|
||||
"invalid public url ${item.publicUrl}",
|
||||
item.publicUrl,
|
||||
"text/html"
|
||||
)
|
||||
Assert.assertFalse("Title is blank at ${item.url}", item.title.isBlank())
|
||||
}
|
||||
|
||||
@@ -51,6 +56,11 @@ class RemoteRepositoryTest(source: MangaSource) : KoinTest {
|
||||
val item = list.random()
|
||||
AssertX.assertUrlRelative("Url is not relative", item.url)
|
||||
AssertX.assertContentType("Bad cover at ${item.url}", item.coverUrl, "image/*")
|
||||
AssertX.assertContentType(
|
||||
"invalid public url ${item.publicUrl}",
|
||||
item.publicUrl,
|
||||
"text/html"
|
||||
)
|
||||
Assert.assertFalse("Title is blank at ${item.url}", item.title.isBlank())
|
||||
}
|
||||
|
||||
@@ -66,6 +76,11 @@ class RemoteRepositoryTest(source: MangaSource) : KoinTest {
|
||||
val item = list.random()
|
||||
AssertX.assertUrlRelative("Url is not relative", item.url)
|
||||
AssertX.assertContentType("Bad cover at ${item.coverUrl}", item.coverUrl, "image/*")
|
||||
AssertX.assertContentType(
|
||||
"invalid public url ${item.publicUrl}",
|
||||
item.publicUrl,
|
||||
"text/html"
|
||||
)
|
||||
Assert.assertFalse("Title is blank at ${item.url}", item.title.isBlank())
|
||||
}
|
||||
|
||||
@@ -74,6 +89,11 @@ class RemoteRepositoryTest(source: MangaSource) : KoinTest {
|
||||
val manga = runBlocking { repo.getList(0) }.random()
|
||||
val details = runBlocking { repo.getDetails(manga) }
|
||||
Assert.assertFalse("No chapters at ${details.url}", details.chapters.isNullOrEmpty())
|
||||
AssertX.assertContentType(
|
||||
"invalid public url ${details.publicUrl}",
|
||||
details.publicUrl,
|
||||
"text/html"
|
||||
)
|
||||
Assert.assertFalse(
|
||||
"Description is empty at ${details.url}",
|
||||
details.description.isNullOrEmpty()
|
||||
|
||||
Reference in New Issue
Block a user