Simply Cosplay (#17291)
This commit is contained in:
parent
15d527bac9
commit
ffd04a5671
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="eu.kanade.tachiyomi.extension">
|
||||||
|
|
||||||
|
<application>
|
||||||
|
<activity
|
||||||
|
android:name=".all.simplycosplay.SimplyCosplayUrlActivity"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:exported="true"
|
||||||
|
android:theme="@android:style/Theme.NoDisplay">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:host="www.simply-cosplay.com" />
|
||||||
|
<data android:host="simply-cosplay.com" />
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:pathPattern="/gallery/..*" />
|
||||||
|
<data android:pathPattern="/image/..*" />
|
||||||
|
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
|
@ -0,0 +1,13 @@
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'kotlinx-serialization'
|
||||||
|
|
||||||
|
ext {
|
||||||
|
extName = 'Simply Cosplay'
|
||||||
|
pkgNameSuffix = 'all.simplycosplay'
|
||||||
|
extClass = '.SimplyCosplay'
|
||||||
|
extVersionCode = 1
|
||||||
|
isNsfw = true
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
|
@ -0,0 +1,382 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.all.simplycosplay
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.preference.ListPreference
|
||||||
|
import androidx.preference.PreferenceScreen
|
||||||
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import okhttp3.HttpUrl
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import rx.Observable
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
import java.io.IOException
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class SimplyCosplay : HttpSource(), ConfigurableSource {
|
||||||
|
|
||||||
|
override val name = "Simply Cosplay"
|
||||||
|
|
||||||
|
override val lang = "all"
|
||||||
|
|
||||||
|
override val baseUrl = "https://www.simply-cosplay.com"
|
||||||
|
|
||||||
|
private val apiUrl = "https://api.simply-porn.com/v2".toHttpUrl()
|
||||||
|
|
||||||
|
override val supportsLatest = true
|
||||||
|
|
||||||
|
override val client = network.cloudflareClient.newBuilder()
|
||||||
|
.addInterceptor(::tokenIntercept)
|
||||||
|
.rateLimit(2)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
override fun headersBuilder() = super.headersBuilder()
|
||||||
|
.add("Referer", baseUrl)
|
||||||
|
|
||||||
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
|
private val preference by lazy {
|
||||||
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tokenIntercept(chain: Interceptor.Chain): Response {
|
||||||
|
val request = chain.request()
|
||||||
|
|
||||||
|
if (request.url.host != apiUrl.host) {
|
||||||
|
return chain.proceed(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
val url = request.url.newBuilder()
|
||||||
|
.setQueryParameter("token", preference.getToken())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val response = chain.proceed(
|
||||||
|
request.newBuilder()
|
||||||
|
.url(url)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (response.isSuccessful.not() && response.code == 403) {
|
||||||
|
response.close()
|
||||||
|
|
||||||
|
val newToken = fetchNewToken()
|
||||||
|
|
||||||
|
preference.putToken(newToken)
|
||||||
|
|
||||||
|
val newUrl = request.url.newBuilder()
|
||||||
|
.setQueryParameter("token", newToken)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return chain.proceed(
|
||||||
|
request.newBuilder()
|
||||||
|
.url(newUrl)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fetchNewToken(): String {
|
||||||
|
val document = client.newCall(GET(baseUrl, headers)).execute().asJsoup()
|
||||||
|
|
||||||
|
val scriptUrl = document.selectFirst("script[src*=main]")
|
||||||
|
?.attr("abs:src")
|
||||||
|
?: throw IOException(TOKEN_EXCEPTION)
|
||||||
|
|
||||||
|
val scriptContent = client.newCall(GET(scriptUrl, headers)).execute()
|
||||||
|
.use { it.body.string() }
|
||||||
|
.replace("\'", "\"")
|
||||||
|
|
||||||
|
return TokenRegex.find(scriptContent)?.groupValues?.get(1)
|
||||||
|
?: throw IOException(TOKEN_EXCEPTION)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun browseUrlBuilder(endPoint: String, sort: String, page: Int): HttpUrl.Builder {
|
||||||
|
return apiUrl.newBuilder().apply {
|
||||||
|
addPathSegment(endPoint)
|
||||||
|
addQueryParameter("sort", sort)
|
||||||
|
addQueryParameter("limit", limit.toString())
|
||||||
|
addQueryParameter("page", page.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun popularMangaRequest(page: Int): Request {
|
||||||
|
val url = browseUrlBuilder(preference.getDefaultBrowse(), "hot", page)
|
||||||
|
|
||||||
|
return GET(url.build(), headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun popularMangaParse(response: Response): MangasPage {
|
||||||
|
runCatching { fetchTags() }
|
||||||
|
|
||||||
|
val result = response.parseAs<browseResponse>()
|
||||||
|
|
||||||
|
val entries = result.data.map(BrowseItem::toSManga)
|
||||||
|
val hasNextPage = result.data.size >= limit
|
||||||
|
|
||||||
|
return MangasPage(entries, hasNextPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun latestUpdatesRequest(page: Int): Request {
|
||||||
|
val url = browseUrlBuilder(preference.getDefaultBrowse(), "new", page)
|
||||||
|
|
||||||
|
return GET(url.build(), headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun latestUpdatesParse(response: Response) = popularMangaParse(response)
|
||||||
|
|
||||||
|
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||||
|
return if (query.startsWith(SEARCH_PREFIX)) {
|
||||||
|
val url = query.substringAfter(SEARCH_PREFIX)
|
||||||
|
val manga = SManga.create().apply { this.url = url }
|
||||||
|
fetchMangaDetails(manga).map {
|
||||||
|
MangasPage(listOf(it), false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
super.fetchSearchManga(page, query, filters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
|
val sort = filters.filterIsInstance<SortFilter>().firstOrNull()?.getSort() ?: "new"
|
||||||
|
|
||||||
|
val url = browseUrlBuilder("search", sort, page).apply {
|
||||||
|
if (query.isNotEmpty()) {
|
||||||
|
addQueryParameter("query", query)
|
||||||
|
}
|
||||||
|
filters.map { filter ->
|
||||||
|
when (filter) {
|
||||||
|
is TagFilter -> {
|
||||||
|
filter.getSelected().forEachIndexed { index, tag ->
|
||||||
|
addQueryParameter(
|
||||||
|
"filter[tag_names][$index]",
|
||||||
|
tag.name.replace(" ", "+"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is TypeFilter -> {
|
||||||
|
filter.getValue().let {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
addQueryParameter("filter[type][0]", it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GET(url.build(), headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaParse(response: Response) = popularMangaParse(response)
|
||||||
|
|
||||||
|
private var tagList: List<String> = emptyList()
|
||||||
|
private var tagsFetchAttempt = 0
|
||||||
|
private var tagsFetchFailed = false
|
||||||
|
|
||||||
|
private fun fetchTags() {
|
||||||
|
if (tagsFetchAttempt < 3 && (tagList.isEmpty() || tagsFetchFailed)) {
|
||||||
|
val tags = runCatching {
|
||||||
|
client.newCall(tagsRequest())
|
||||||
|
.execute().use(::tagsParse)
|
||||||
|
}
|
||||||
|
|
||||||
|
tagsFetchFailed = tags.isFailure
|
||||||
|
tagList = tags.getOrElse {
|
||||||
|
Log.e("SimplyHentaiTags", it.stackTraceToString())
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
tagsFetchAttempt++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tagsRequest(): Request {
|
||||||
|
val url = apiUrl.newBuilder()
|
||||||
|
.addPathSegment("search")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return GET(url, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tagsParse(response: Response): List<String> {
|
||||||
|
val result = response.parseAs<TagsResponse>()
|
||||||
|
|
||||||
|
return result.aggs.tag_names.buckets.map {
|
||||||
|
it.key.trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Tag(name: String) : Filter.CheckBox(name)
|
||||||
|
|
||||||
|
class TagFilter(title: String, tags: List<String>) :
|
||||||
|
Filter.Group<Tag>(title, tags.map(::Tag)) {
|
||||||
|
|
||||||
|
fun getSelected() = state.filter { it.state }
|
||||||
|
}
|
||||||
|
|
||||||
|
class TypeFilter(title: String, private val types: List<String>) :
|
||||||
|
Filter.Select<String>(title, types.toTypedArray()) {
|
||||||
|
|
||||||
|
fun getValue() = types[state].lowercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
class SortFilter(title: String, private val sorts: List<String>) :
|
||||||
|
Filter.Select<String>(title, sorts.toTypedArray()) {
|
||||||
|
|
||||||
|
fun getSort() = sorts[state].lowercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFilterList(): FilterList {
|
||||||
|
val filters: MutableList<Filter<*>> = mutableListOf(
|
||||||
|
SortFilter("Sort", listOf("New", "Hot")),
|
||||||
|
TypeFilter("Type", listOf("", "Image", "Gallery")),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (tagList.isNotEmpty()) {
|
||||||
|
filters += TagFilter("Tags", tagList)
|
||||||
|
} else {
|
||||||
|
filters += listOf(
|
||||||
|
Filter.Separator(),
|
||||||
|
Filter.Header("Press 'Reset' to attempt to show tags"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return FilterList(filters)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mangaUrlBuilder(dbUrl: String): HttpUrl.Builder {
|
||||||
|
val pathSegments = dbUrl.split("/")
|
||||||
|
val type = pathSegments[1]
|
||||||
|
val slug = pathSegments[3]
|
||||||
|
|
||||||
|
return apiUrl.newBuilder().apply {
|
||||||
|
addPathSegment(type)
|
||||||
|
addPathSegments(slug)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||||
|
val url = mangaUrlBuilder(manga.url)
|
||||||
|
|
||||||
|
return GET(url.build(), headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mangaDetailsParse(response: Response): SManga {
|
||||||
|
val result = response.parseAs<detailsResponse>()
|
||||||
|
|
||||||
|
return result.data.toSManga()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMangaUrl(manga: SManga) = baseUrl + manga.url
|
||||||
|
|
||||||
|
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||||
|
return Observable.just(
|
||||||
|
listOf(
|
||||||
|
SChapter.create().apply {
|
||||||
|
url = manga.url
|
||||||
|
name = manga.url.split("/")[1].replaceFirstChar {
|
||||||
|
if (it.isLowerCase()) {
|
||||||
|
it.titlecase(
|
||||||
|
Locale.ROOT,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
it.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
date_upload = manga.description?.substringAfterLast("Date: ").parseDate()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getChapterUrl(chapter: SChapter) = baseUrl + chapter.url
|
||||||
|
|
||||||
|
override fun pageListRequest(chapter: SChapter): Request {
|
||||||
|
val url = mangaUrlBuilder(chapter.url)
|
||||||
|
|
||||||
|
return GET(url.build(), headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
|
val result = response.parseAs<pageResponse>()
|
||||||
|
|
||||||
|
return result.data.images?.mapIndexedNotNull { index, image ->
|
||||||
|
if (image.urls.url.isNullOrEmpty()) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
Page(index, "", image.urls.url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?: Page(1, "", result.data.preview.urls.url).let(::listOf)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
|
ListPreference(screen.context).apply {
|
||||||
|
key = BROWSE_TYPE_PREF_KEY
|
||||||
|
title = BROWSE_TYPE_TITLE
|
||||||
|
entries = arrayOf("Gallery", "Image")
|
||||||
|
entryValues = arrayOf("gallery", "image")
|
||||||
|
summary = "%s"
|
||||||
|
setDefaultValue("gallery")
|
||||||
|
}.also(screen::addPreference)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SharedPreferences.getDefaultBrowse() =
|
||||||
|
getString(BROWSE_TYPE_PREF_KEY, "gallery")!!
|
||||||
|
|
||||||
|
private fun SharedPreferences.getToken() =
|
||||||
|
getString(DEFAULT_TOKEN_PREF, DEFAULT_FALLBACK_TOKEN) ?: DEFAULT_FALLBACK_TOKEN
|
||||||
|
|
||||||
|
private fun SharedPreferences.putToken(token: String) =
|
||||||
|
edit().putString(DEFAULT_TOKEN_PREF, token).commit()
|
||||||
|
|
||||||
|
private inline fun <reified T> Response.parseAs(): T {
|
||||||
|
return json.decodeFromString(body.string())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String?.parseDate(): Long {
|
||||||
|
return runCatching { dateFormat.parse(this!!)!!.time }
|
||||||
|
.getOrDefault(0L)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val limit = 20
|
||||||
|
const val SEARCH_PREFIX = "url:"
|
||||||
|
|
||||||
|
private const val DEFAULT_TOKEN_PREF = "default_token_pref"
|
||||||
|
private const val DEFAULT_FALLBACK_TOKEN = "01730876"
|
||||||
|
private const val TOKEN_EXCEPTION = "Unable to fetch new Token"
|
||||||
|
private val TokenRegex = Regex("""token\s*:\s*"([^\"]+)""")
|
||||||
|
|
||||||
|
private val dateFormat by lazy { SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss.SSS", Locale.ENGLISH) }
|
||||||
|
|
||||||
|
private const val BROWSE_TYPE_PREF_KEY = "default_browse_type_key"
|
||||||
|
private const val BROWSE_TYPE_TITLE = "Default Browse List"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun chapterListParse(response: Response) =
|
||||||
|
throw UnsupportedOperationException("Not implemented")
|
||||||
|
|
||||||
|
override fun imageUrlParse(response: Response) =
|
||||||
|
throw UnsupportedOperationException("Not implemented")
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.all.simplycosplay
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
|
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
typealias browseResponse = Data<List<BrowseItem>>
|
||||||
|
|
||||||
|
typealias detailsResponse = Data<DetailsResponse>
|
||||||
|
|
||||||
|
typealias pageResponse = Data<PageResponse>
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Data<T>(val data: T)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class BrowseItem(
|
||||||
|
val title: String? = null,
|
||||||
|
val slug: String,
|
||||||
|
val type: String,
|
||||||
|
val preview: Images,
|
||||||
|
) {
|
||||||
|
fun toSManga() = SManga.create().apply {
|
||||||
|
title = this@BrowseItem.title ?: ""
|
||||||
|
url = "/${type.lowercase().trim()}/new/$slug"
|
||||||
|
thumbnail_url = preview.urls.thumb.url
|
||||||
|
description = preview.publish_date?.let { "Date: $it" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class TagsResponse(
|
||||||
|
val aggs: Agg,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Agg(
|
||||||
|
val tag_names: TagNames,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class TagNames(
|
||||||
|
val buckets: List<GenreItem>,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class GenreItem(
|
||||||
|
val key: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class DetailsResponse(
|
||||||
|
val title: String? = null,
|
||||||
|
val slug: String,
|
||||||
|
val type: String,
|
||||||
|
val preview: Images,
|
||||||
|
val tags: List<Tag>? = emptyList(),
|
||||||
|
val image_count: Int? = null,
|
||||||
|
) {
|
||||||
|
fun toSManga() = SManga.create().apply {
|
||||||
|
title = this@DetailsResponse.title ?: ""
|
||||||
|
url = "/${type.lowercase().trim()}/new/$slug"
|
||||||
|
thumbnail_url = preview.urls.thumb.url
|
||||||
|
genre = tags?.mapNotNull { it ->
|
||||||
|
it.name?.trim()?.split(" ")?.let { genre ->
|
||||||
|
genre.map {
|
||||||
|
it.replaceFirstChar { char ->
|
||||||
|
if (char.isLowerCase()) {
|
||||||
|
char.titlecase(
|
||||||
|
Locale.ROOT,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
char.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}?.joinToString(" ")
|
||||||
|
}?.joinToString()
|
||||||
|
description = buildString {
|
||||||
|
append("Type: $type\n")
|
||||||
|
image_count?.let { append("Images: $it\n") }
|
||||||
|
preview.publish_date?.let { append("Date: $it\n") }
|
||||||
|
}
|
||||||
|
update_strategy = UpdateStrategy.ONLY_FETCH_ONCE
|
||||||
|
status = SManga.COMPLETED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class PageResponse(
|
||||||
|
val images: List<Images>? = null,
|
||||||
|
val preview: Images,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Images(
|
||||||
|
val publish_date: String? = null,
|
||||||
|
val urls: Urls,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Urls(
|
||||||
|
val url: String? = null,
|
||||||
|
val thumb: Url,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Url(
|
||||||
|
val url: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Tag(
|
||||||
|
val name: String? = null,
|
||||||
|
)
|
|
@ -0,0 +1,33 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.all.simplycosplay
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
class SimplyCosplayUrlActivity : Activity() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
val pathSegments = intent?.data?.pathSegments
|
||||||
|
if (pathSegments != null && pathSegments.size >= 3) {
|
||||||
|
val mainIntent = Intent().apply {
|
||||||
|
action = "eu.kanade.tachiyomi.SEARCH"
|
||||||
|
putExtra("query", "${SimplyCosplay.SEARCH_PREFIX}/${pathSegments[0]}/new/${pathSegments[2]}")
|
||||||
|
putExtra("filter", packageName)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
startActivity(mainIntent)
|
||||||
|
} catch (e: ActivityNotFoundException) {
|
||||||
|
Log.e("SimplyCosplayUrlActivit", e.toString())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e("SimplyCosplayUrlActivit", "could not parse uri from intent $intent")
|
||||||
|
}
|
||||||
|
|
||||||
|
finish()
|
||||||
|
exitProcess(0)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue