Update korean extension, Initial Support for Naver Comic (#1087)

* Update korean extension, Rename MSM to ManaMoa. (Need to migrate)

JMana: Fix errors due to site changes
NewToki: Supports baseUrl override. update domain,

MSM (ManaMoa)
----
MSM now uses id on manga instead of title name. (It breaks whole things.)
I don't want to increase `versionId` so I rename extension.
(I renamed while ago and reverted because it wasn't need on that time)
So result is need to migrate from MangaShow.Me to ManaMoa.
MangaShow.Me is now deprecated and will remain as dummy before 1.2.15

* Fix Image request on old chapters due to site changes + mistake on ManaMoa extension

* Refactor ManaMoa ImageUrlHandler

* Initial Support for Naver Comic

* Update Icons, Support Challenge Webtoons

Note that (normal) Challenge webtoons has chapter bug rn. dunno why.
This commit is contained in:
DitFranXX 2019-05-14 08:25:21 +09:00 committed by Eugene
parent 48d9b2829d
commit 9d5b774cc2
26 changed files with 476 additions and 94 deletions

View File

@ -5,7 +5,7 @@ ext {
appName = 'Tachiyomi: JMana'
pkgNameSuffix = 'ko.jmana'
extClass = '.JMana'
extVersionCode = 7
extVersionCode = 8
libVersion = '1.2'
}

View File

@ -40,7 +40,7 @@ class JMana : ParsedHttpSource() {
override fun popularMangaNextPageSelector() = "div.page > ul > li"
// Do not add page parameter if page is 1 to prevent tracking.
override fun popularMangaRequest(page: Int) = GET("$baseUrl/comic_main_frame?page=${page - 1}")
override fun popularMangaRequest(page: Int) = GET("$baseUrl/comic_main_frame?tag=null&keyword=null&chosung=null&page=${page - 1}")
override fun popularMangaParse(response: Response): MangasPage {
val document = response.asJsoup()
@ -59,33 +59,41 @@ class JMana : ParsedHttpSource() {
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
override fun searchMangaNextPageSelector() = popularMangaSelector()
override fun searchMangaParse(response: Response) = popularMangaParse(response)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = GET("$baseUrl/comic_main_frame?keyword=$query&page=${page - 1}")
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = GET("$baseUrl/?tag=null&keyword=$query&chosung=null&page=${page - 1}")
override fun mangaDetailsParse(document: Document): SManga {
val info = document.select("div.leftM").first()
val authorText = info.select("div.comBtnArea a").text()
val titleDescription = info.select("li.row")
val descriptionElement = document.select(".media > .row > .media-body.col-9 > div")
val thumbnailUrl = document.select(".media > .row > .media-body.col-3 img.media-object-list").attr("src")
val manga = SManga.create()
manga.title = titleDescription.first().text()
manga.description = titleDescription.last().text()
manga.author = authorText
descriptionElement
.map { it.text() }
.forEach { text ->
when {
DETAIL_TITLE in text -> manga.title = text.substringAfter(DETAIL_TITLE).trim()
DETAIL_AUTHOR in text -> manga.author = text.substringAfter(DETAIL_AUTHOR).trim()
DETAIL_GENRE in text -> manga.genre = text.substringAfter("장르 : [").substringBefore("]").trim()
DETAIL_DESCRIPTION in text -> text.substringAfter(DETAIL_DESCRIPTION).trim()
}
}
manga.thumbnail_url = thumbnailUrl
manga.status = SManga.UNKNOWN
return manga
}
override fun chapterListSelector() = "div.contents > ul > li"
override fun chapterListSelector() = "div.section > .post > .post-content-list"
override fun chapterFromElement(element: Element): SChapter {
val linkElement = element.select("a")
val linkElement = element.select(".entry-title a")
val rawName = linkElement.text()
val chapterUrl = "${linkElement.attr("href")}?viewstyle=list".replace("book/", "book_frame/")
val chapter = SChapter.create()
chapter.url = linkElement.attr("href").replace("book/", "book_frame/")
chapter.url = chapterUrl
chapter.chapter_number = parseChapterNumber(rawName)
chapter.name = rawName.trim()
chapter.date_upload = parseChapterDate(element.select("ul > li:not(.fcR)").last().text())
chapter.date_upload = parseChapterDate(element.select("li.publish-date span").last().text())
return chapter
}
@ -105,7 +113,7 @@ class JMana : ParsedHttpSource() {
private fun parseChapterDate(date: String): Long {
return try {
SimpleDateFormat("yyyy-MM-dd").parse(date).time
SimpleDateFormat("yyyy-MM-dd HH:mm").parse(date).time
} catch (e: Exception) {
e.printStackTrace()
0
@ -146,4 +154,11 @@ class JMana : ParsedHttpSource() {
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("This method should not be called!")
override fun getFilterList() = FilterList()
companion object {
const val DETAIL_TITLE = "제목 : "
const val DETAIL_GENRE = "장르 : "
const val DETAIL_AUTHOR = "작가 : "
const val DETAIL_DESCRIPTION = "설명 : "
}
}

View File

@ -2,10 +2,10 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
appName = 'Tachiyomi: MangaShow.Me (ManaMoa)'
appName = 'Tachiyomi: ManaMoa (MangaShow.Me)'
pkgNameSuffix = 'ko.mangashowme'
extClass = '.MangaShowMe'
extVersionCode = 11
extClass = '.MSMFactory'
extVersionCode = 12
libVersion = '1.2'
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -0,0 +1,44 @@
package eu.kanade.tachiyomi.extension.ko.mangashowme
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.online.HttpSource
import okhttp3.Response
/*
* Source Factory for before-Migration
*
* I will remove this and only use ManaMoa class after 1.2.15.
* This is just helper who uses =<1.2.11 before.
*
*/
class MSMFactory : SourceFactory {
override fun createSources(): List<Source> = listOf(
ManaMoa(),
MSMDeprecated()
)
}
class MSMDeprecated : HttpSource() {
override val name = "MangaShow.Me"
override val baseUrl: String = "https://Depricated._Need.Source.Migration.to.ManaMoa.net"
override val lang: String = "ko"
override val supportsLatest = false
override fun chapterListParse(response: Response) = throw Exception(NEED_MIGRATION)
override fun popularMangaRequest(page: Int) = throw Exception(NEED_MIGRATION)
override fun popularMangaParse(response: Response) = throw Exception(NEED_MIGRATION)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw Exception(NEED_MIGRATION)
override fun searchMangaParse(response: Response) = throw Exception(NEED_MIGRATION)
override fun latestUpdatesRequest(page: Int) = throw Exception(NEED_MIGRATION)
override fun latestUpdatesParse(response: Response) = throw Exception(NEED_MIGRATION)
override fun mangaDetailsParse(response: Response) = throw Exception(NEED_MIGRATION)
override fun imageUrlParse(response: Response) = throw Exception(NEED_MIGRATION)
override fun pageListParse(response: Response) = throw Exception(NEED_MIGRATION)
companion object {
const val NEED_MIGRATION = "Deprecated: Use 'ManaMoa' instead.\nSource migration is on 'My Library' -> three dots -> 'Source migration'"
}
}

View File

@ -73,9 +73,10 @@ internal class ImageDecoderInterceptor : Interceptor {
*
* MIT License
*
*
* Copyright (c) 2019 junheah
*/
private fun imageDecoder(input: Bitmap, chapter: Int, view_cnt: Int, half: Int = 0, _CX: Int = MangaShowMe.V1_CX, _CY: Int = MangaShowMe.V1_CY): Bitmap {
private fun imageDecoder(input: Bitmap, chapter: Int, view_cnt: Int, half: Int = 0, _CX: Int = ManaMoa.V1_CX, _CY: Int = ManaMoa.V1_CY): Bitmap {
if (view_cnt == 0) return input
val viewCnt = view_cnt / 10
var CX = _CX

View File

@ -0,0 +1,57 @@
package eu.kanade.tachiyomi.extension.ko.mangashowme
import okhttp3.Interceptor
import okhttp3.Response
internal class ImageUrlHandlerInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response = RequestHandler(chain).run()
}
private class RequestHandler(val chain: Interceptor.Chain) {
val req = chain.request()
val origUrl = req.url().toString()
fun run(): Response {
// only for image Request
if (req.header("ImageRequest") != "1") return chain.proceed(req)
val secondUrl = req.header("SecondUrlToRequest")
val res = getRequest(origUrl)
return if (!isSuccess(res) && secondUrl != null) {
getRequest(secondUrl)
} else res
}
private fun isSuccess(res: Response): Boolean {
val length = res.header("content-length")?.toInt() ?: 0
return !(!res.isSuccessful || length < 50000)
}
private fun getRequest(url: String): Response = when {
"filecdn.xyz" in url || "chickencdn.info" in url
-> ownCDNRequestHandler(url)
else -> outsideRequestHandler(url)
}
private fun ownCDNRequestHandler(url: String): Response {
val res = proceedRequest(url)
return if (!isSuccess(res)) {
proceedRequest(url.replace("img.", "s3.")) // s3
} else res
}
private fun outsideRequestHandler(url: String): Response {
val outUrl = url.substringBefore("?quick")
return proceedRequest(outUrl)
}
private fun proceedRequest(url: String): Response = chain.proceed(
req.newBuilder()!!
.url(url)
.removeHeader("ImageRequest")
.removeHeader("ImageDecodeRequest")
.removeHeader("SecondUrlToRequest")
.build()!!
)
}

View File

@ -26,13 +26,17 @@ import java.util.*
import java.util.concurrent.TimeUnit
/**
* MangaShow.Me Source
* ManaMoa Source
*
* Originally it was mangashow.me extension but they changed site structure widely.
* so I moved to new name for treating as new source.
* Users who uses =<1.2.11 need to migrate sources. starts 1.2.12
*
* PS. There's no Popular section. It's just a list of manga. Also not latest updates.
* `manga_list` returns latest 'added' manga. not a chapter updates.
**/
class MangaShowMe : ConfigurableSource, ParsedHttpSource() {
override val name = "MangaShow.Me"
class ManaMoa : ConfigurableSource, ParsedHttpSource() {
override val name = "ManaMoa"
private val defaultBaseUrl = "https://manamoa3.net"
override val baseUrl by lazy { getPrefBaseUrl() }
override val lang: String = "ko"
@ -43,52 +47,7 @@ class MangaShowMe : ConfigurableSource, ParsedHttpSource() {
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(ImageDecoderInterceptor())
.addInterceptor { chain ->
val req = chain.request()
// only for image Request
val isFileCdn = !req.url().host().contains(".filecdn.xyz")
if (!req.url().toString().endsWith("?quick")) return@addInterceptor chain.proceed(req)
val secondUrl = req.header("SecondUrlToRequest")
fun get(flag: Int = 0): Request {
val url = if (isFileCdn) {
when (flag) {
1 -> req.url().toString().replace("img.", "s3.")
else -> req.url().toString()
}
} else {
when (flag) {
1 -> secondUrl!!
2 -> secondUrl!!.replace("img.", "s3.")
else -> req.url().toString().substringBefore("?quick")
}
}
return req.newBuilder()!!.url(url)
.removeHeader("ImageDecodeRequest")
.removeHeader("SecondUrlToRequest")
.build()!!
}
val res = chain.proceed(get())
if (isFileCdn) {
val length = res.header("content-length")
if (length == null || length.toInt() < 50000) {
chain.proceed(get(1)) // s3
} else res
} else {
if (!res.isSuccessful && secondUrl != null) {
val fallbackRes = chain.proceed(get(1)) // img filecdn
val fallbackLength = fallbackRes.header("content-length")
if (fallbackLength == null || fallbackLength.toInt() < 50000) {
chain.proceed(get(2)) // s3
} else fallbackRes
} else res
}
}
.addInterceptor(ImageUrlHandlerInterceptor())
.build()!!
override fun popularMangaSelector() = "div.manga-list-gallery > div > div.post-row"
@ -98,7 +57,7 @@ class MangaShowMe : ConfigurableSource, ParsedHttpSource() {
val titleElement = element.select(".manga-subject > a").first()
val manga = SManga.create()
manga.url = urlTitleEscape(linkElement.attr("href"))
manga.url = linkElement.attr("href")
manga.title = titleElement.text().trim()
manga.thumbnail_url = urlFinder(element.select(".img-wrap-back").attr("style"))
return manga
@ -244,21 +203,15 @@ class MangaShowMe : ConfigurableSource, ParsedHttpSource() {
val decoder = ImageDecoder(element)
if (imageUrls.length() != imageUrls1.length()) {
(0 until imageUrls.length())
.map { imageUrls.getString(it) }
.forEach { pages.add(Page(pages.size, decoder.request(it), "${it.substringBefore("!!")}?quick")) }
} else {
(0 until imageUrls1.length())
.map {
imageUrls1.getString(it) + try {
"!!${imageUrls.getString(it)}?quick"
} catch (_: Exception) {
""
}
(0 until imageUrls.length())
.map {
imageUrls.getString(it) + try {
"!!${imageUrls1.getString(it)}?quick"
} catch (_: Exception) {
""
}
.forEach { pages.add(Page(pages.size, decoder.request(it), "${it.substringBefore("!!")}?quick")) }
}
}
.forEach { pages.add(Page(pages.size, decoder.request(it), "${it.substringBefore("!!")}?quick")) }
} catch (e: Exception) {
e.printStackTrace()
}
@ -284,7 +237,7 @@ class MangaShowMe : ConfigurableSource, ParsedHttpSource() {
builder.build()!!
} catch (_: Exception) {
headers
}
}.newBuilder()!!.add("ImageRequest", "1").build()!!
return GET(page.imageUrl!!, requestHeaders)
}
@ -306,14 +259,6 @@ class MangaShowMe : ConfigurableSource, ParsedHttpSource() {
return style.substringAfter("background-image:url(").substringBefore(")")
}
// Some title contains `&` and `#` which can cause a error.
private fun urlTitleEscape(title: String): String {
val url = title.split("&manga_name=")
return "${url[0]}&manga_name=" +
url[1].replace("&", "%26").replace("#", "%23")
}
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}

View File

@ -0,0 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
appName = 'Tachiyomi: Naver Comic'
pkgNameSuffix = 'ko.navercomic'
extClass = '.NaverComicFactory'
extVersionCode = 1
libVersion = '1.2'
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -0,0 +1,74 @@
package eu.kanade.tachiyomi.extension.ko.navercomic
import android.annotation.SuppressLint
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import okhttp3.Request
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
class NaverWebtoon : NaverComicBase("webtoon") {
override val name = "Naver Webtoon"
override fun popularMangaRequest(page: Int) = GET("$baseUrl/$mType/weekday.nhn")
override fun popularMangaSelector() = ".list_area.daily_all .col ul > li"
override fun popularMangaNextPageSelector() = null
override fun popularMangaFromElement(element: Element): SManga {
val thumb = element.select("div.thumb img").first().attr("src")
val title = element.select("a.title").first()
val manga = SManga.create()
manga.url = title.attr("href").substringBefore("&week")
manga.title = title.text().trim()
manga.thumbnail_url = thumb
return manga
}
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/$mType/weekday.nhn?order=Update")
override fun latestUpdatesSelector() = ".list_area.daily_all .col.col_selected ul > li"
override fun latestUpdatesNextPageSelector() = null
override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element)
}
class NaverBestChallenge : NaverComicChallengeBase("bestChallenge") {
override val name = "Naver Webtoon Best Challenge"
override fun popularMangaRequest(page: Int) = GET("$baseUrl/genre/$mType.nhn")
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/genre/$mType.nhn?m=main&order=Update")
}
class NaverChallenge : NaverComicChallengeBase("challenge") {
override val name = "Naver Webtoon Challenge"
override fun popularMangaRequest(page: Int) = GET("$baseUrl/genre/$mType.nhn")
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/genre/$mType.nhn?m=list&order=Update")
// Need to override again because there's no mobile page.
override fun chapterPagedListRequest(manga: SManga, page: Int): Request {
return GET("$baseUrl${manga.url}&page=$page")
}
override fun chapterListSelector() = ".viewList > tbody > tr:not([class])"
override fun chapterFromElement(element: Element): SChapter {
val nameElement = element.select("td.title > a").first()
val rawName = nameElement.text().trim()
val chapter = SChapter.create()
chapter.url = nameElement.attr("src")
chapter.chapter_number = parseChapterNumber(rawName)
chapter.name = rawName
chapter.date_upload = parseChapterDate(element.select("td.num").last().text().trim())
return chapter
}
@SuppressLint("SimpleDateFormat")
private fun parseChapterDate(date: String): Long {
return try {
SimpleDateFormat("yyyy.MM.dd").parse(date).time
} catch (e: Exception) {
e.printStackTrace()
0
}
}
}

View File

@ -0,0 +1,170 @@
package eu.kanade.tachiyomi.extension.ko.navercomic
import android.annotation.SuppressLint
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.FilterList
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.ParsedHttpSource
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import java.text.SimpleDateFormat
abstract class NaverComicBase(protected val mType: String) : ParsedHttpSource() {
override val lang: String = "ko"
override val baseUrl: String = "https://comic.naver.com"
private val mobileUrl = "https://m.comic.naver.com"
override val supportsLatest = true
override val client: OkHttpClient = network.client
private val mobileHeaders = super.headersBuilder()
.add("Referer", mobileUrl)
.build()
override fun searchMangaSelector() = ".resultList > li h5 > a"
override fun searchMangaNextPageSelector() = ".paginate a.next"
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = GET("search.nhn?m=$mType&keyword=$query&type=title&page=$page")
override fun searchMangaFromElement(element: Element): SManga {
val url = element.attr("href").substringBefore("&week").substringBefore("&listPage=")
val manga = SManga.create()
manga.url = url
manga.title = element.text().trim()
return manga
}
override fun chapterListSelector() = "#ct > .toon_lst.lst2 > li > div a"
// Need to override because the chapter list is paginated.
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> = fetchChapterList(manga, 1)
private fun fetchChapterList(manga: SManga, page: Int,
pastChapters: List<SChapter> = emptyList()): Observable<List<SChapter>> {
val chapters = pastChapters.toMutableList()
fun isSamePage(list: List<SChapter>): Boolean = try {
chapters.last().url == list.last().url
} catch (_: Exception) {
false
}
return fetchChapterListPage(manga, page)
.flatMap {
if (isSamePage(it)) {
Observable.just(chapters)
} else {
chapters += it
fetchChapterList(manga, page + 1, chapters)
}
}
}
private fun fetchChapterListPage(manga: SManga, page: Int): Observable<List<SChapter>> {
return client.newCall(chapterPagedListRequest(manga, page))
.asObservableSuccess()
.map { response ->
chapterListParse(response)
}
}
override fun chapterListRequest(manga: SManga): Request {
return chapterPagedListRequest(manga, 1)
}
open fun chapterPagedListRequest(manga: SManga, page: Int): Request {
return GET("$mobileUrl${manga.url}&page=$page", mobileHeaders)
}
override fun chapterFromElement(element: Element): SChapter {
val rawName = element.select(".toon_name > strong").last().ownText()
val url = element.attr("href").substringBefore("&week").substringBefore("&listPage")
val chapter = SChapter.create()
chapter.url = url
chapter.chapter_number = parseChapterNumber(rawName)
chapter.name = rawName
chapter.date_upload = parseChapterDate(element.select(".toon_detail_info .if1").last().text().trim())
return chapter
}
protected fun parseChapterNumber(name: String): Float {
try {
if (name.contains("[단편]")) return 1f
// `특별` means `Special`, so It can be buggy. so pad `편`(Chapter) to prevent false return
if (name.contains("번외") || name.contains("특별편")) return -2f
val regex = Regex("([0-9]+)(?:[-.]([0-9]+))?(?:화)")
val (ch_primal, ch_second) = regex.find(name)!!.destructured
return (ch_primal + if (ch_second.isBlank()) "" else ".$ch_second").toFloatOrNull() ?: -1f
} catch (e: Exception) {
e.printStackTrace()
return -1f
}
}
@SuppressLint("SimpleDateFormat")
private fun parseChapterDate(date: String): Long {
return try {
SimpleDateFormat("YY.MM.dd").parse(date).time
} catch (e: Exception) {
e.printStackTrace()
0
}
}
override fun mangaDetailsParse(document: Document): SManga {
val element = document.select(".comicinfo")
val titleElement = element.select(".detail > h2")
val manga = SManga.create()
manga.title = titleElement.first().ownText().trim()
manga.author = titleElement.select("span").text().trim()
manga.description = document.select(".comicinfo > p").text().trim()
manga.thumbnail_url = element.select(".thumb > a > img").last().attr("src")
return manga
}
override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>()
try {
document.select(".wt_viewer img")
.map {
it.attr("src")
}
.forEach {
pages.add(Page(pages.size, "", it))
}
} catch (e: Exception) {
e.printStackTrace()
}
return pages
}
//We are able to get the image URL directly from the page list
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("This method should not be called!")
override fun getFilterList() = FilterList()
}
abstract class NaverComicChallengeBase(mType: String) : NaverComicBase(mType) {
override fun popularMangaSelector() = ".weekchallengeBox tbody td:not([class])"
override fun popularMangaNextPageSelector(): String? = ".paginate .page_wrap a.next"
override fun popularMangaFromElement(element: Element): SManga {
val thumb = element.select("a img").first().attr("src")
val title = element.select(".challengeTitle a").first()
val manga = SManga.create()
manga.url = title.attr("href").substringBefore("&week")
manga.title = title.text().trim()
manga.thumbnail_url = thumb
return manga
}
override fun latestUpdatesSelector() = popularMangaSelector()
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element)
}

View File

@ -0,0 +1,12 @@
package eu.kanade.tachiyomi.extension.ko.navercomic
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
class NaverComicFactory : SourceFactory {
override fun createSources(): List<Source> = listOf(
NaverWebtoon(),
NaverBestChallenge(),
NaverChallenge()
)
}

View File

@ -5,8 +5,13 @@ ext {
appName = 'Tachiyomi: NewToki'
pkgNameSuffix = 'ko.newtoki'
extClass = '.NewTokiFactory'
extVersionCode = 7
extVersionCode = 8
libVersion = '1.2'
}
dependencies {
compileOnly project(':preference-stub')
compileOnly 'com.github.inorichi.injekt:injekt-core:65b0440'
}
apply from: "$rootDir/common.gradle"

View File

@ -1,7 +1,14 @@
package eu.kanade.tachiyomi.extension.ko.newtoki
import android.annotation.SuppressLint
import android.app.Application
import android.content.SharedPreferences
import android.support.v7.preference.EditTextPreference
import android.support.v7.preference.PreferenceScreen
import android.widget.Toast
import eu.kanade.tachiyomi.extension.BuildConfig
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
@ -10,13 +17,16 @@ import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.text.SimpleDateFormat
import java.util.*
/**
* NewToki Source
**/
open class NewToki(override val name: String, override val baseUrl: String, private val boardName: String) : ParsedHttpSource() {
open class NewToki(override val name: String, private val defaultBaseUrl: String, private val boardName: String) : ConfigurableSource, ParsedHttpSource() {
override val baseUrl by lazy { getPrefBaseUrl() }
override val lang: String = "ko"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
@ -183,4 +193,41 @@ open class NewToki(override val name: String, override val baseUrl: String, priv
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("This method should not be called!")
override fun getFilterList() = FilterList()
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val baseUrlPref = EditTextPreference(screen.context).apply {
key = BASE_URL_PREF_TITLE
title = BASE_URL_PREF_TITLE
summary = BASE_URL_PREF_SUMMARY
this.setDefaultValue(defaultBaseUrl)
dialogTitle = BASE_URL_PREF_TITLE
dialogMessage = "Default: $defaultBaseUrl"
setOnPreferenceChangeListener { _, newValue ->
try {
val res = preferences.edit().putString(BASE_URL_PREF, newValue as String).commit()
Toast.makeText(screen.context, RESTART_TACHIYOMI, Toast.LENGTH_LONG).show()
res
} catch (e: Exception) {
e.printStackTrace()
false
}
}
}
screen.addPreference(baseUrlPref)
}
private fun getPrefBaseUrl(): String = preferences.getString(BASE_URL_PREF, defaultBaseUrl)
companion object {
private const val BASE_URL_PREF_TITLE = "Override BaseUrl"
private const val BASE_URL_PREF = "overrideBaseUrl_v${BuildConfig.VERSION_NAME}"
private const val BASE_URL_PREF_SUMMARY = "For temporary uses. Update extension will erase this setting."
private const val RESTART_TACHIYOMI = "Restart Tachiyomi to apply new setting."
}
}

View File

@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.source.model.FilterList
import okhttp3.HttpUrl
import okhttp3.Request
private const val baseDomain = "newtoki10"
private const val baseDomain = "newtoki12"
class NewTokiFactory : SourceFactory {
override fun createSources(): List<Source> = listOf(